Django中有两种视图,一种是函数式视图,另一种是类视图。视图的作用主要是,用于填充逻辑,返回响应体。函数式视图难以扩展,代码复用率低。而类视图可以利用继承、Mixins,快速复用、扩展功能。本文主要讨论了,Django对类视图的处理逻辑,类视图装饰器实现。
1. Django的视图
Django的URL解析器,将一个HttpRequest对象和相应的参数传递给一个可调用的函数,并期待其返回一个HttpResponse对象。这个可调用的函数,就是视图函数。
1.1 函数式视图
views.py
1
2
3
4
5
6
7
8
9
| 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
1
2
3
4
5
6
7
| # 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
1
2
3
4
5
6
7
8
9
10
| 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
1
2
3
4
5
6
7
| # 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方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
| @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也接受列表参数,可以同时装配多个装饰器。
1
2
3
4
5
6
7
8
| 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方法。
1
2
3
4
5
6
7
8
9
| 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中配置装饰器
1
2
3
4
5
6
7
8
9
| 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. 参考