django之類檢視
類檢視
1 類檢視引入
以函式的方式定義的檢視稱為函式檢視,函式檢視便於理解。但是遇到一個檢視對應的路徑提供了多種不同HTTP請求方式的支援時,便需要在一個函式中編寫不同的業務邏輯,程式碼可讀性與複用性都不佳。
def register(request):
"""處理註冊""" # 獲取請求方法,判斷是GET/POST請求 if request.method == 'GET': # 處理GET請求,返回註冊頁面 return render(request, 'register.html') else: # 處理POST請求,實現註冊邏輯 return HttpResponse('這裡實現註冊邏輯')
在Django中也可以使用類來定義一個檢視,稱為類檢視。
使用類檢視可以將檢視對應的不同請求方式以類中的不同方法來區別定義。如下所示
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('這裡實現註冊邏輯')
類檢視的好處:
- 程式碼可讀性好
- 類檢視相對於函式檢視有更高的複用性, 如果其他地方需要用到某個類檢視的某個特定邏輯,直接繼承該類檢視即可
2 類檢視使用
定義類檢視需要繼承自Django提供的父類View,可使用from django.views.generic import View
或者from django.views.generic.base import View
匯入,定義方式如上所示。
配置路由時,使用類檢視的as_view()
方法來新增。
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 類檢視使用裝飾器
為類檢視新增裝飾器,可以使用三種方法。
為了理解方便,我們先來定義一個為函式檢視準備的裝飾器(在設計裝飾器時基本都以函式檢視作為考慮的被裝飾物件),及一個要被裝飾的類檢視。
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配置中,單看檢視的時候無法知道此檢視還被添加了裝飾器,不利於程式碼的完整性,不建議使用。
此種方式會為類檢視中的所有請求方法都加上裝飾器行為(因為是在檢視入口處,分發請求方式前)。
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擴充套件類,也會為類檢視的所有請求方法都新增裝飾行為。