django 的类视图和函数视图-杂谈

  • 在django很老的版本时候,只有function-based views,但问题是是基于函数的视图太过于简单,很难去拓展,自定义它们,没法达到视图重用的地步。

  • 为了解决这个问题,class-based views诞生了。所以,现在的django有基于函数或者基于类这两种视图。

更换网站函数视图为类视图

url和视图关系

一般先打开浏览器,然后在浏览器的地址栏里输入一个网址,也就是URL,然后回车,我们就可以在浏览器里看到这个网址返回的内容。这是我们能看得见的过程,还有一些我们看不见的过程,那就是:当我们在浏览器里输入网址(URL)时,回车,然后浏览器就会向目标网址发送一个HTTP请求,服务器收到请求之后就会给这个请求做出一个响应,这个响应就是把对应的内容通过浏览器渲染出来,呈现给我们看。

这个过程就是请求与响应:

简单来说也就是

浏览器发送请求给服务器服务器对请求进行处理再返回http相应最后返回html文档给前端

请求和响应

表现形式如下:

urlpatterns = [

    path(正则表达式, views视图函数,参数,别名),

]

括号里的参数说明:

1、一个正则表达式字符串

2、一个可调用对象,通常为一个视图函数或一个指定视图函数路径的字符串

3、可选的要传递给视图函数的默认参数(字典形式)

4、一个可选的name参数(别名)

比如我想构造三个URL,网站首页(http://www.django.cn/)、新闻(http://www.django.cn/news/)、论坛(http://www.django.cn/bbs/),我们可以这么做

urlpatterns = [

path('', views.index), #里面留空,代表首页

path('news/',views.news),#news

path('bbs/',views.bbs),#bbs

]

URL就是这么构造的,我们的域名www.django.cn不需要写,完整的URL应该要这么写:path(正则表达式, views视图函数,参数,别名), 里面的正则表达式, views视图函数,是必须要写的,而参数,别名是可选的。我们在有特殊需要的时候才写。关于URL详细介绍和使用方法可以查看文章:路由配置系统URLconf

通过上面我们可以看到,每个URL都对应一个views视图函数名,视图函数名不能相同,否则会报错。视图函数,Django中约定写在APP应用里的views.py文件里。然后在urls.py文件里通过下面的方式导入:

from APP应用名 import views

from APP应用名.vews import 函数名或类名

视图函数是一个简单的Python 函数,它接受Web请求并且返回Web响应。响应可以是一张网页的HTML内容,一个重定向,一个404错误,一个XML文档,或者一张图片. . . 是任何东西都可以。无论视图本身包含什么逻辑,都要返回响应。这个视图函数代码一般约定是放置在项目或应用程序目录中的名为views.py的文件中。

http请求中产生两个核心对象:

1、http请求---->HttpRequest对象,用户请求相关的所有信息(对象)

2、http响应---->HttpResponse对象,响应字符串

之前我们在欢迎页面这章的时候有操作过一次。我们回顾一下:

首先,打开打开bolg目录下的views.py文件,写一个hello视图函数,在里面输入:

from django.http import HttpResponse

def hello(request):

   """

  写一个hello函数,通过request接收URL或者说是http请求信息,

  然后给这个请求返回一个HttpResponse对象

  """

    return HttpResponse('欢迎使用Django!')

例子里,我们用到的request,就是HttpRequest对象。HttpResponse("欢迎使用Django!"),就是HttpRequest对象,它向http请求响应了一段字符串对象。

我们打开myblog目录下的urls.py文件中先导入视图函数,然后构造一个URL,代码如下:

from blog import views  #导入视图函数

urlpatterns = [

    ...

    path('', views.hello),   #这个是我们构造的URL

]

代码写完之后,启动项目就可以在浏览器里看到视图函数返回的字符串"欢迎使用Django!"

每一个URL都会对应一个视图函数,当一个用户请求访问Django站点的一个页面时,然后就由Django路由系统(URL配置文件)去决定要执行哪个视图函数使用的算法。

通过URL对应关系匹配 ->找到对应的函数(或者类)->返回字符串(或者读取Html之后返回渲染的字符串)这个过程也就是我们Django请求的生命周期。

视图函数,就是围绕着HttpRequest和HttpResponse这两个对象进行的。

类视图和函数视图的区别

函数视图

以函数的方式定义的视图称为函数视图,函数视图便于理解。但是遇到一个视图对应的路径提供了多种不同HTTP请求方式的支持时,便需要在一个函数中编写不同的业务逻辑,代码可读性与复用性都不佳。

比如我之前网站就是用到的函数视图:

from django.shortcuts import render

from apps.blog.models import Article, Category, Tag

from django.core.paginator import Paginator, PageNotAnInteger, EmptyPage

from django.http import Http404

from django.conf import settings

categories = Category.objects.all()  # 获取全部的分类对象

tags = Tag.objects.all()  # 获取全部的标签对象

months = Article.objects.datetimes('pub_time', 'month', order='DESC')

# Create your views here.

def home(request):  # 主页

    posts = Article.objects.filter(status='p', pub_time__isnull=False)  # 获取全部(状态为已发布,发布时间不为空)Article对象

    paginator = Paginator(posts, settings.PAGE_NUM)  # 每页显示数量

    page = request.GET.get('page')  # 获取URL中page参数的值

    try:

        post_list = paginator.page(page)

    except PageNotAnInteger:

        post_list = paginator.page(1)

    except EmptyPage:

        post_list = paginator.page(paginator.num_pages)

    return render(request, 'home.html', {'post_list': post_list, 'category_list': categories, 'months': months})

def detail(request, id):

    try:

        post = Article.objects.get(id=str(id))

        post.viewed()  # 更新浏览次数

        tags = post.tags.all()

        next_post = post.next_article()  # 上一篇文章对象

        prev_post = post.prev_article()  # 下一篇文章对象

    except Article.DoesNotExist:

        raise Http404

    return render(

        request, 'post.html',

        {

            'post': post,

            'tags': tags,

            'category_list': categories,

            'next_post': next_post,

            'prev_post': prev_post,

            'months': months

        }

    )

def search_category(request, id):

    posts = Article.objects.filter(category_id=str(id))

    category = categories.get(id=str(id))

    paginator = Paginator(posts, settings.PAGE_NUM)  # 每页显示数量

    try:

        page = request.GET.get('page')  # 获取URL中page参数的值

        post_list = paginator.page(page)

    except PageNotAnInteger:

        post_list = paginator.page(1)

    except EmptyPage:

        post_list = paginator.page(paginator.num_pages)

    return render(request, 'category.html',

                  {'post_list': post_list,

                   'category_list': categories,

                   'category': category,

                   'months': months

                  }

    )

def search_tag(request, tag):

    posts = Article.objects.filter(tags__name__contains=tag)

    paginator = Paginator(posts, settings.PAGE_NUM)  # 每页显示数量

    try:

        page = request.GET.get('page')  # 获取URL中page参数的值

        post_list = paginator.page(page)

    except PageNotAnInteger:

        post_list = paginator.page(1)

    except EmptyPage:

        post_list = paginator.page(paginator.num_pages)

    return render(request, 'tag.html', {

        'post_list': post_list,

        'category_list': categories,

        'tag': tag,

        'months': months

        }

    )

def archives(request, year, month):

    posts = Article.objects.filter(pub_time__year=year, pub_time__month=month).order_by('-pub_time')

    paginator = Paginator(posts, settings.PAGE_NUM)  # 每页显示数量

    try:

        page = request.GET.get('page')  # 获取URL中page参数的值

        post_list = paginator.page(page)

    except PageNotAnInteger:

        post_list = paginator.page(1)

    except EmptyPage:

        post_list = paginator.page(paginator.num_pages)

    return render(request, 'archive.html', {

        'post_list': post_list,

        'category_list': categories,

        'months': months,

        'year_month': year+'年'+month+'月'

        }

    )

但是后面在写评论、登陆等等功能的时候,感觉业务逻辑处理的很乱,而且我也很迫切地想尽快把网站地功能做的更完善,就搞得几天都摸不着头绪不知道怎么前进。也可能是我一开始写的时候,没有想那么远,后来越做越复杂了,在日后的开放中也感觉类视图的方便。于是我就换成了类视图:

import markdown

import time

from django.views import generic

from django.conf import settings

from django.utils.text import slugify

from django.shortcuts import render, HttpResponse, render_to_response

from django.views.decorators.csrf import csrf_exempt

from django.shortcuts import get_object_or_404, get_list_or_404

from .models import Article, Category, Tag

from markdown.extensions.toc import TocExtension  # 锚点的拓展

from django.core.cache import cache

# Create your views here.

class IndexView(generic.ListView):

    model = Article

    template_name = 'index.html'

    context_object_name = 'articles'

    paginate_by = getattr(settings, 'BASE_PAGE_BY', None)

    paginate_orphans = getattr(settings, 'BASE_ORPHANS', 0)

    def get_ordering(self):

        ordering = super(IndexView, self).get_ordering()

        sort = self.kwargs.get('sort')

        if sort == 'v':

            return ('-views', '-update_date', '-id')

        return ordering

class DetailView(generic.DetailView):

    model = Article

    template_name = 'article.html'

    context_object_name = 'article'

    def get_object(self):

        obj = super(DetailView, self).get_object()

        # 设置浏览量增加时间判断,同一篇文章两次浏览超过半小时才重新统计阅览量,作者浏览忽略

        u = self.request.user

        ses = self.request.session

        the_key = 'is_read_{}'.format(obj.id)

        is_read_time = ses.get(the_key)

        if u != obj.author:

            if not is_read_time:

                obj.update_views()

                ses[the_key] = time.time()

            else:

                now_time = time.time()

                t = now_time - is_read_time

                if t > 60 * 30:

                    obj.update_views()

                    ses[the_key] = time.time()

        # 文章可以使用markdown书写,带格式的文章,像csdn写markdown文章一样

        # md = markdown.Markdown(extensions=[

        #     'markdown.extensions.extra',

        #     'markdown.extensions.codehilite',

        #     TocExtension(slugify=slugify),

        # ])

        # obj.body = md.convert(obj.body)

        # obj.toc = md.toc

        return obj

class CategoryView(generic.ListView):

    model = Article

    template_name = 'category.html'

    context_object_name = 'articles'

    paginate_by = getattr(settings, 'BASE_PAGE_BY', None)

    paginate_orphans = getattr(settings, 'BASE_ORPHANS', 0)

    def get_ordering(self):

        ordering = super(CategoryView, self).get_ordering()

        sort = self.kwargs.get('sort')

        if sort == 'v':

            return ('-views', '-update_date', '-id')

        return ordering

    def get_queryset(self, **kwargs):

        queryset = super(CategoryView, self).get_queryset()

        cate = get_object_or_404(Category, slug=self.kwargs.get('slug'))

        return queryset.filter(category=cate)

    def get_context_data(self, **kwargs):

        context_data = super(CategoryView, self).get_context_data()

        cate = get_object_or_404(Category, slug=self.kwargs.get('slug'))

        context_data['search_tag'] = '文章分类'

        context_data['search_instance'] = cate

        return context_data

def global_setting(request):

    return{

        "AUTHOR_NAME" : settings.AUTHOR_NAME,

        "AUTHOR_DESC" : settings.AUTHOR_DESC,

        "AUTHOR_EMAIL" : settings.AUTHOR_EMAIL,

        "AUTHOR_TITLE" : settings.AUTHOR_TITLE,

    }

def md2html(request):   #md2html工具页

    return render(request, 'tools_html/md2html.html', 

        { 

        }

    )

除了个别的网站还是用的函数以外基本网站的主体都换成了类视图。

他们在rul的配置上也是有区别的:

普通视图函数:

from .views import index

urlpatterns = [  url(r'^index/$', index, name='index'),]

通用类视图:

from .views import ClassListView

urlpatterns = [ url(r'^index/$', ClassListView.as_view(), name='index'),]

使用函数式图的时候要定义多个views可以使用 from xx import xx as x1这样可以避免视图的重复

比如在model里可以给类写一个get_ordering函数然后根据前端给的参数不同在不同地来进行排序。

之前地界面中由于后端地功能性太少了,就导致前端里地计算也很繁琐。

反正当我开发评论功能地时候就乱成一锅粥了。

  • 类视图相比函数视图的化重复性更高一些,使用函数式图的时候总感觉只能render一个界面,导致后面网站更加复杂后,业务逻辑关系就很复杂了。

最后,感谢tendcode的帮助和解答。。

本作品采用《CC 协议》,转载必须注明作者和本文链接
文章!!首发于我的博客Stray_Camel(^U^)ノ~YO
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!