1 類視圖引入
以函數的方式定義的視圖稱爲函數視圖,函數視圖便於理解。html
可是遇到一個視圖對應的路徑提供了多種不一樣HTTP請求方式的支持時,便須要在一個函數中編寫不一樣的業務邏輯,代碼可讀性與複用性都不佳。django
def register(request): """處理註冊""" # 獲取請求方法,判斷是GET/POST請求 if request.method == 'GET': # 處理GET請求,返回註冊頁面 return render(request, 'register.html') else: # 處理POST請求,實現註冊邏輯 return HttpResponse('這裏實現註冊邏輯')
在Django中也可使用類來定義一個視圖,稱爲類視圖。app
使用類視圖能夠將視圖對應的不一樣請求方式以類中的不一樣方法來區別定義。以下所示函數
from django.views.generic import View class RegisterView(View): """類視圖:處理註冊""" def get(self, request): """處理GET請求,返回註冊頁面""" return render(request, 'register.html') def post(self, request): """處理POST請求,實現註冊邏輯""" return HttpResponse('這裏實現註冊邏輯')
類視圖的好處:post
- 代碼可讀性好
- 類視圖相對於函數視圖有更高的複用性, 若是其餘地方須要用到某個類視圖的某個特定邏輯,直接繼承該類視圖便可
2 類視圖使用
定義類視圖須要繼承自Django提供的父類View,可以使用from django.views.generic import View
或者from django.views.generic.base import View
導入,定義方式如上所示。url
配置路由時,使用類視圖的as_view()
方法來添加。spa
urlpatterns = [ # 視圖函數:註冊 # url(r'^register/$', views.register, name='register'), # 類視圖:註冊 url(r'^register/$', views.RegisterView.as_view(), name='register'), ]
3 類視圖原理
@classonlymethod def as_view(cls, **initkwargs): """ Main entry point for a request-response process. """ ...省略代碼... 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 # 調用dispatch方法,按照不一樣請求方式調用不一樣請求方法 return self.dispatch(request, *args, **kwargs) ...省略代碼... # 返回真正的函數視圖 return view def dispatch(self, request, *args, **kwargs): # Try to dispatch to the right method; if a method doesn't exist, # defer to the error handler. Also defer to the error handler if the # request method isn't on the approved list. 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)
4 類視圖使用裝飾器
爲類視圖添加裝飾器,可使用三種方法。設計
爲了理解方便,咱們先來定義一個爲函數視圖準備的裝飾器(在設計裝飾器時基本都以函數視圖做爲考慮的被裝飾對象),及一個要被裝飾的類視圖。code
def my_decorator(func): def wrapper(request, *args, **kwargs): print('自定義裝飾器被調用了') print('請求路徑%s' % request.path) return func(request, *args, **kwargs) return wrapper class DemoView(View): def get(self, request): print('get方法') return HttpResponse('ok') def post(self, request): print('post方法') return HttpResponse('ok')
4.1 在URL配置中裝飾
urlpatterns = [ url(r'^demo/$', my_decorate(DemoView.as_view())) ]
此種方式最簡單,但因裝飾行爲被放置到了url配置中,單看視圖的時候沒法知道此視圖還被添加了裝飾器,不利於代碼的完整性,不建議使用。htm
此種方式會爲類視圖中的全部請求方法都加上裝飾器行爲(由於是在視圖入口處,分發請求方式前)。
4.2 在類視圖中裝飾
在類視圖中使用爲函數視圖準備的裝飾器時,不能直接添加裝飾器,須要使用method_decorator將其轉換爲適用於類視圖方法的裝飾器。
from django.utils.decorators import method_decorator # 爲所有請求方法添加裝飾器 class DemoView(View): @method_decorator(my_decorator) def dispatch(self, *args, **kwargs): return super().dispatch(*args, **kwargs) def get(self, request): print('get方法') return HttpResponse('ok') def post(self, request): print('post方法') return HttpResponse('ok') # 爲特定請求方法添加裝飾器 class DemoView(View): @method_decorator(my_decorator) def get(self, request): print('get方法') return HttpResponse('ok') def post(self, request): print('post方法') return HttpResponse('ok')
method_decorator裝飾器還支持使用name參數指明被裝飾的方法
# 爲所有請求方法添加裝飾器 @method_decorator(my_decorator, name='dispatch') class DemoView(View): def get(self, request): print('get方法') return HttpResponse('ok') def post(self, request): print('post方法') return HttpResponse('ok') # 爲特定請求方法添加裝飾器 @method_decorator(my_decorator, name='get') class DemoView(View): def get(self, request): print('get方法') return HttpResponse('ok') def post(self, request): print('post方法') return HttpResponse('ok')
爲何須要使用method_decorator???
爲函數視圖準備的裝飾器,其被調用時,第一個參數用於接收request對象
def my_decorate(func): def wrapper(request, *args, **kwargs): # 第一個參數request對象 ...代碼省略... return func(request, *args, **kwargs) return wrapper
而類視圖中請求方法被調用時,傳入的第一個參數不是request對象,而是self 視圖對象自己,第二個位置參數纔是request對象
class DemoView(View): def dispatch(self, request, *args, **kwargs): ...代碼省略... def get(self, request): ...代碼省略...
因此若是直接將用於函數視圖的裝飾器裝飾類視圖方法,會致使參數傳遞出現問題。
method_decorator的做用是爲函數視圖裝飾器補充第一個self參數,以適配類視圖方法。
若是將裝飾器自己改成能夠適配類視圖方法的,相似以下,則無需再使用method_decorator。
def my_decorator(func): def wrapper(self, request, *args, **kwargs): # 此處增長了self print('自定義裝飾器被調用了') print('請求路徑%s' % request.path) return func(self, request, *args, **kwargs) # 此處增長了self return wrapper
4.3 構造Mixin擴展類
使用面向對象多繼承的特性。
class MyDecoratorMixin(object): @classmethod def as_view(cls, *args, **kwargs): view = super().as_view(*args, **kwargs) view = my_decorator(view) return view class DemoView(MyDecoratorMixin, View): def get(self, request): print('get方法') return HttpResponse('ok') def post(self, request): print('post方法') return HttpResponse('ok')
使用Mixin擴展類,也會爲類視圖的全部請求方法都添加裝飾行爲。