博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Django Class Based View
阅读量:4680 次
发布时间:2019-06-09

本文共 12860 字,大约阅读时间需要 42 分钟。

本节内容

  

  

  

  

  

  

 

一  Class Based View 基于类的视图

 

 

function view 存在问题,无法继承复用,尤其时框架封装好的类用不了,function组装复用更擅长

class based view 代码更简洁

简单样例

version 1

# urls.pyfrom django.conf.urls import urlfrom mysite import views as my_viewurlpatterns = [    url(r'^about/', my_view.about),]# mysite/views.pyfrom django.shortcuts import renderdef about(request):    return render(request, 'about.html')

基本的function based view

version 2

# urls.pyfrom django.views.generic import TemplateViewurlpatterns = [    url(r'^about/', TemplateView.as_view(template_name='about.html')),]# mysite/views.pyfrom django.shortcuts import renderdef about(request):    return render(request, 'about.html')

TemplateView.as_view(template_name='about.html'))  方法 as_view(),参数template_name=‘网页模板’

version 3

# mysite/views.pyfrom django.views.generic import TemplateViewclass AboutView(TemplateView):    template_name = 'about.html'#mysite/urls.pyfrom mysite import views as my_viewurlpatterns = [    url(r'^about/', my_view.AboutView.as_view()),]

说明:

  • as_view()返回的是一个function object

  • 模板名字作为as_view参数传进去,也可以作为类变量设置template_name = '网页模板'

 

1.  类的视图 View

# mysite/views.py def my_view(request):    if request.method == 'GET':        return render(request, 'about.html')    elif request.method == 'POST':        return HttpResponse('post it')    elif request.method == 'HEAD':        return HttpResponse('head it')
# mysite/urls.pyfrom mysite import viewsurlpatterns = [    url(r'^about/', views.my_view),]

等价于 类图公共基类View

# mysite/views.pyfrom django.shortcuts import render,HttpResponsefrom django.views.generic import Viewclass MyView(View):    def get(self, request):        return render(request, 'about.html')    def post(self, request):        return HttpResponse('post it')    def head(self, request):        return HttpResponse('head it')# mysite/urls.pyfrom mysite import views as my_viewurlpatterns = [    url(r'^about/', my_view.MyView.as_view()),]

 

类视图好处就是可以直接继承和覆盖

# mysite/views.pyfrom django.views.generic import Viewclass GreetingView(View):    greeting = 'Good Day'    def get(self, request):        return HttpResponse(self.greeting)class MorningGreeting(GreetingView):    greeting = 'Morning to ya'# mysite/urls.pyurlpatterns = [my_view.GreetingView.as_view(greeting="G'day")),]

 

源码分析

class View(object):    # 所有views的公共类,仅仅实现 dispatch方法和简单安全性验证    http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']    def __init__(self, **kwargs):        for key, value in six.iteritems(kwargs):            setattr(self, key, value)    @classonlymethod    def as_view(cls, **initkwargs):        # request-response的主入口点                for key in initkwargs:            # 不能有get,post等方法名的参数,后续有setattr()操作,会覆盖原始请求方法            if key in cls.http_method_names:                raise TypeError("You tried to pass in the %s method name as a "                                "keyword argument to %s(). Don't do that."                                % (key, cls.__name__))            # 如果传入了一个没有定义的类属性,就报错;如有greeting属性,就不报错            if not hasattr(cls, key):                raise TypeError("%s() received an invalid keyword %r. as_view "                                "only accepts arguments that are already "                                "attributes of the class." % (cls.__name__, key))        # 定义了类似闭包的视图函数        def view(request, *args, **kwargs):            self = cls(**initkwargs)  # 调用__init__()进行实例化            if hasattr(self, 'get') and not hasattr(self, 'head'):                self.head = self.get            self.request = request            self.args = args            self.kwargs = kwargs            return self.dispatch(request, *args, **kwargs) # 调用 self.dispatch()进行处理        # 对view object设置了一些属性        view.view_class = cls        view.view_initkwargs = initkwargs        # 更新文档说明        update_wrapper(view, cls, updated=())        # and possible attributes set by decorators        # like csrf_exempt from dispatch        update_wrapper(view, cls.dispatch, assigned=())        return view    def dispatch(self, request, *args, **kwargs):        # 将request.method反射到类相应的方法上,并执行        if request.method.lower() in self.http_method_names:            # 如果有如get,就handler=get;如果没有handler = self.http_method_not_allowed            handler = getattr(self, request.method.lower(), self.http_method_not_allowed)        else:            handler = self.http_method_not_allowed        return handler(request, *args, **kwargs)    def http_method_not_allowed(self, request, *args, **kwargs):        logger.warning(            'Method Not Allowed (%s): %s', request.method, request.path,            extra={
'status_code': 405, 'request': request} ) return http.HttpResponseNotAllowed(self._allowed_methods()) def options(self, request, *args, **kwargs): """ Handles responding to requests for the OPTIONS HTTP verb. """ response = http.HttpResponse() response['Allow'] = ', '.join(self._allowed_methods()) response['Content-Length'] = '0' return response def _allowed_methods(self): return [m.upper() for m in self.http_method_names if hasattr(self, m)]
类View
#!/usr/bin/env python3# -*-coding:utf-8 -*-# __author__:Jonathan# email:nining1314@gmail.comfrom django.shortcuts import HttpResponsefrom django import httpfrom django.utils import sixfrom django.utils.decorators import classonlymethodfrom django.conf.urls import urlclass View(object):    http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']    def __init__(self, **kwargs):        for key, value in six.iteritems(kwargs):            setattr(self, key, value)    @classonlymethod    def as_view(cls, **initkwargs):        for key in initkwargs:            if key in cls.http_method_names:                raise TypeError("禁止 %s 作为 %s()关键字参数" % (key, cls.__name__))            if not hasattr(cls, key):                raise TypeError("类 %s() 接收到非法参数 %s,只接受类存在类属性" % (cls.__name__, key))                def view(request, *args, **kwargs):            # 把 as_view()传入的关键字参数,赋值为相应的类变量            self = cls(**initkwargs)  # 类的实例化,虽然只是一行代码,要理解原理:是有很多行代码执行完的结果            if hasattr(self, 'get') and not hasattr(self, 'head'):                self.head = self.get            self.request = request            self.args = args            self.kwargs = kwargs            return self.dispatch(request, *args, **kwargs)  # 一行代码,代表的是self.dispatch()的执行结果                # 函数也是一种对象,可以有自己的属性****        view.view_class = cls        view.view_initkwargs = initkwargs                # 返回闭合的函数对象        return view        def dispatch(self, request, *args, **kwargs):        if request.method.lower() in self.http_method_names:            handler = getattr(self, request.method.lower(), self.http_method_not_allowed)        else:            handler = self.http_method_not_allowed        return handler(request, *args, **kwargs)   # 一行代码,代表的是handler()的执行结果    """最终dispatch结果:根据request.method.lower()得到相应的http请求方法,反射到同名类方法,执行"""            def http_method_not_allowed(self, request, *args, **kwargs):        return http.HttpResponseNotAllowed(self._allowed_methods())        def _allowed_methods(self):        return [m.upper() for m in self.http_method_names if hasattr(self, m)]class MyView(View):    def get(self, request):        return HttpResponse('get it')    def post(self, request):        return HttpResponse('post it')    def head(self, request):        return HttpResponse('head it')urlpatterns = [    url(r'^my_view/', MyView.as_view()),]
吃透类View

说明: 

  • as_view返回一个 view function 

  • as_view接收参数是可以覆盖类定义的变量

  • __init__检查as_view传入的参数是否在类中定义

  • View.as_view()返回函数视图的view 对象,说明类视图只是对原有的函数视图进行了封装,而没有否定函数view的作用

  • view function运行时会调用dispatch,根据用户的request.method路由到get, post等方法

最终得出结论:

View类 在没有更改原Django逻辑的情况下,可以用类来编写view,每个http请求会使用对应类的同名方法进行处理

 

2.  类的视图 TemplateView

class ContextMixin(object):    # 渲染模板时,处理需要的上下文参数    def get_context_data(self, **kwargs):        if 'view' not in kwargs:            kwargs['view'] = self        return kwargsclass TemplateResponseMixin(object):    # 渲染模板,Mixin一般是小功能类封装,被其他类继承    template_name = None    template_engine = None    response_class = TemplateResponse    content_type = None    def render_to_response(self, context, **response_kwargs):        # 进行模板渲染,render()是个函数,response_class()是个类,实现本质目标没多大区别        response_kwargs.setdefault('content_type', self.content_type)        return self.response_class(            request=self.request,            template=self.get_template_names(),            context=context,            using=self.template_engine,            **response_kwargs        )    def get_template_names(self):        if self.template_name is None:            raise ImproperlyConfigured(                "TemplateResponseMixin requires either a definition of "                "'template_name' or an implementation of 'get_template_names()'")        else:            return [self.template_name]class TemplateView(TemplateResponseMixin, ContextMixin, View):    # 实现get()方法,有渲染上文参数,有渲染操作    def get(self, request, *args, **kwargs):        context = self.get_context_data(**kwargs)        return self.render_to_response(context)
类TemplateView

说明:

  • 每个Mixin只提供部分功能,最终需要类整合

  • TemplateResponseMixin提供render_to_response() 方法,渲染模板

  • ContentMixin提供get_context_data()方法,提供渲染数据

  • View提供get,post用户访问接口

  • 面向对象编程:如何拆分功能模块,如何组装功能模块

 

 

3.  类的视图 login_required解决方法

3.1  封装Mixin(推荐)

from django.contrib.auth.decorators import login_requiredclass LoginRequiredMixin(object):    @classmethod    def as_view(cls, **initkwargs):        view = super(LoginRequiredMixin, cls).as_view(**initkwargs)        return login_required(view)class MyView(LoginRequiredMixin, ...):    pass

3.2 装饰类

from django.contrib.auth.decorators import login_requiredfrom django.utils.decorators import method_decoratorfrom django.views.generic import TemplateViewclass ProtectedView(TemplateView):    template_name = 'secret.html'    @method_decorator(login_required)    def dispatch(self, request, *args, **kwargs):        return super(ProtectedView, self).dispatch(*args, **kwargs)

 

推荐学习网站:http://devdocs.io/

 

二  通用视图

 

 

通用视图(generic class base view) 和 class base view 概念上不是一回事

Class Base View 是指用类的方式去写视图

通用视图 是用Class Base View的方式将我们常用的增、删、改、查封装成可扩展的类,使用时直接继承、快速实现

 

1.  通用视图 - ListView

配置编程,获取数据列表

from django.db import modelsclass Publisher(models.Model):    name = models.CharField(max_length=30)    address = models.CharField(max_length=50)    city = models.CharField(max_length=60)    state_province = models.CharField(max_length=30)    country = models.CharField(max_length=50)    website = models.URLField()    def __str__(self):        return self.nameclass Author(models.Model):    first_name = models.CharField(max_length=30)    last_name = models.CharField(max_length=40)    email = models.EmailField()    def __str__(self):        return '%s %s' % (self.first_name, self.last_name)class Book(models.Model):    title = models.CharField(max_length=100)    authors = models.ManyToManyField('Author')    publisher = models.ForeignKey('Publisher')    publication_date = models.DateField(auto_now_add=True)    def __str__(self):        return self.title
models.py
from django.views.generic import ListViewfrom .models import Publisherclass PublisherList(ListView):    model = Publisher  # 类属性:指定解析的model    # queryset = Publisher.objects.all()[0:1]      # 和model二选一,获取指定数据    context_object_name = 'publishers'             # 默认object_list,前端渲染时的上下文参数    # template_name = 'books/publisher_list.html'  # 指定渲染的模板文件,默认值model名小写_list.html    # 联系类的queryset,而该方法为实例的定制    def get_queryset(self):        print(self.request)  # 根据 self.request进行判断,返回符合条件的queryset        return Publisher.objects.all()[0:1]
urlpatterns = [     url(r'^publishers/$', views.PublisherList.as_view(), name='publishers'), ]

说明:

  • multi object list view

  • model、queryset和get_queryset三者之间关系

  • content_object_name

  • template_name

源码UML图分析:

2.  通用视图 - DetailView

显示一个object的详情页面

from django.views.generic import DetailViewfrom .models import Publisherfrom .models import Bookclass PublisherDetail(DetailView):    model = Publisher    context_object_name = 'publisher'  # 模板渲染对象,默认值object    def get_context_data(self, **kwargs):        context = super(PublisherDetail, self).get_context_data(**kwargs)        context['book_list'] = Book.objects.all()  # 附加额外选软数据        return context    '''    # 默认的object,是从url里的pk获取的    def get_object(self, queryset=None):        object = super(PublisherDetail, self).get_object()        # 进行更新操作        # object.last_accessed = timezone.now()        # object.save()        return object'''
urlpatterns = [     url(r'^publisher/(?P
[0-9]+)/$', views.PublisherDetail.as_view(), name='publisher-detail'), ]

说明:

  • Single object detail view

  • get_context_data

  • content_object_name

  • get_object

源码UML图分析:

 

 

3.  通用视图 - FormView

  • FormView

  • CreateView,UpdateView,DeleteView

转载于:https://www.cnblogs.com/jonathan1314/p/7545298.html

你可能感兴趣的文章
P1298(矩阵切割)DP
查看>>
wzplayer for delphi demo截图
查看>>
团队第二周:SRS文档
查看>>
Zookeeper的安装与使用:
查看>>
密码策略限制最大与最小长度
查看>>
正则表达式模式
查看>>
使用iframe实现同域跨站提交数据
查看>>
Mouse点击之后,复制GridView控件的数据行
查看>>
ASP.NET开发,从二层至三层,至面向对象 (2)
查看>>
如何查看自己电脑支持OpenGL core版本
查看>>
页面元素定位 XPath 简介
查看>>
[转]loadrunner:系统的平均并发用户数和并发数峰值如何估算
查看>>
Linux下Tomcat重新启动
查看>>
HTML Table to Json
查看>>
Theano 学习笔记(一)
查看>>
1.7 节点进行排序显示
查看>>
web最佳实践
查看>>
spring 集成shiro 之 自定义过滤器
查看>>
验证密码不允许有连续三位重复的正则表达式
查看>>
python 中对list去重
查看>>