目录

    Django中有两种视图,一种是函数式视图,另一种是类视图。视图的作用主要是,用于填充逻辑,返回响应体。函数式视图难以扩展,代码复用率低。而类视图可以利用继承、Mixins,快速复用、扩展功能。本文主要讨论了,Django对类视图的处理逻辑,类视图装饰器实现。

    1. Django的视图

    Django的URL解析器,将一个HttpRequest对象和相应的参数传递给一个可调用的函数,并期待其返回一个HttpResponse对象。这个可调用的函数,就是视图函数。

    1.1 函数式视图

    views.py

    from django.http import HttpResponse
    
    def my_view(request):
        if request.method == 'GET':
            # 填充逻辑
            return HttpResponse('result')
        if request.method == 'POST':
            # 填充逻辑
            return HttpResponse('result')
    

    urls.py

    # urls.py
    from django.conf.urls import patterns
    import .views as home_view
    
    urlpatterns = patterns('',
        (r'^my_view/', home_view.my_view)
    )
    

    函数式视图(FBV)只使用在定制错误,或那些使用类视图实现时会很复杂的情况。

    1.2 类视图

    views.py

    from django.http import HttpResponse
    from django.views.generic.base import View
    
    class MyView(View):
        def get(self, request):
            # 填充逻辑
            return HttpResponse('result')
        def post(self, request):
            # 填充逻辑
            return HttpResponse('result')
    

    urls.py

    # urls.py
    from django.conf.urls import patterns
    from .views import MyView
    
    urlpatterns = patterns('',
        (r'^about/', MyView.as_view()),
    )
    

    为了解耦视图和URL、代码复用,Django提供了类视图。

    类视图(CBV)提供了一个as_view()静态方法,调用该方法,会创建一个类的实例。然后调用实例的dispatch()方法,dispatch()方法会根据request的请求类型,调用相应实例的同名方法。如果没有找到对应的方法,将引发一个HttpResponseNotAllowed异常。

    Django提供的一系列类视图类,都继承自一个View基类(django.views.generic.base.View),在这个基类里实现了与 URL的接口(as_view)、请求方法匹配(dispatch)和一些其他的基本功能。比如 ,RedirectView 实现了 HTTP 重定向,TemplateView 新增了渲染模板的方法。

    django.views.generic.base.py 中View类as_view和dispatch方法

    @classonlymethod
    def as_view(cls, **initkwargs):
        for key in initkwargs:
            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__))
            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)
            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)
    
        update_wrapper(view, cls, updated=())
    
        update_wrapper(view, cls.dispatch, assigned=())
        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)
    

    2. Django视图Mixin类

    Django将基本的HTTP请求和响应,抽象封装成类。在使用过程中,只需要将这些基类聚合,按照需求的方式重写或者直接复用。这些基类称之为Mixin。

    在django.views.generic包中,除了base提供了构成cbv最基础的几个Mixin,以及cbv的基类View,还提供了四个模块,

    • detail,显示详细数据,SingleObjectMixin,SingleObjectTemplateResponseMixin
    • list,显示列表,MultipleObjectMixin,MultipleObjectTemplateResponseMixin
    • edit,提供新增和编辑的功能,DeletionMixin,FormMixin,
    • dates,年月日相关的显示和获取,YearMixin, MonthMixin, DayMixin, WeekMixin, DateMixin

    一个View Class可以继承多个Mixin,但是只能继承一个View(包括其子类)。

    3. 类视图装饰器

    3.1 修饰dispatch

    dispatch装饰器,将影响到全部的视图类方法函数。Django 1.9开始method_decorator支持name参数,可以指定方法,例如,name=’get’,表示仅修饰get函数。如果有多个装饰器需要配置,@method_decorator也接受列表参数,可以同时装配多个装饰器。

    from django.contrib.auth.decorators import login_required
    from django.utils.decorators import method_decorator
    from django.views.generic.base import View
    
    class MyView(View):
        @method_decorator(login_required)
        def dispatch(self, *args, **kwargs):
            return super(MyView, self).dispatch(*args, **kwargs)
    

    带参数的装饰器用法: @method_decorator(login_required_by_role(‘super’))

    3.2 修饰视图类

    修饰类和修饰dispatch一样,也会影响全部HTTP方法。

    from django.contrib.auth.decorators import login_required
    from django.utils.decorators import method_decorator
    from django.views.generic.base import View
    
    @method_decorator(login_required)
    class MyView(View): 
    
        def dispatch(self, *args, **kwargs):
            return super(MyView, self).dispatch(*args, **kwargs)
    

    3.3 URLConf中配置装饰器

    from django.contrib.auth.decorators import login_required, permission_required
    from django.views.generic import TemplateView
    
    from .views import VoteView
    
    urlpatterns = [
        url(r'^about/$', login_required(TemplateView.as_view(template_name="secret.html"))),
        url(r'^vote/$', permission_required('polls.can_vote')(VoteView.as_view())),
    ]
    

    4. 参考