本文是對 Django 官網(wǎng)文檔的翻譯。
官網(wǎng)鏈接:https://docs.djangoproject.com/en/1.11/topics/class-based-views/intro/
適用版本:Django1.11
類視圖提供了一種使用 Python 對象實現(xiàn)視圖的方法呻疹。類視圖不會替代函數(shù)視圖璧疗, 與函數(shù)視圖相比毫深,它們有一定的差異和優(yōu)勢:
采用單獨的函數(shù)來處理特定 HTTP 請求(GET坡疼,POST等)代替函數(shù)視圖的條件分支不恭。
可以使用面向?qū)ο蟮募夹g(shù)歌憨,如 mixins(多重繼承)將代碼重新定義為可重用的組件着憨。
通用視圖、類視圖和通用類視圖之間的關(guān)系和歷史
最初务嫡,只有視圖函數(shù)甲抖,Django 將 HttpRequest 傳遞給函數(shù)并且期望返回一個 HttpResponse 。 這是 Django 的工作范圍心铃。
早期的開發(fā)發(fā)現(xiàn)了視圖中共同的習(xí)語和模式准谚,因此引入了函數(shù)通用視圖來抽象這些模式,從而簡化常見視圖開發(fā)去扣。
函數(shù)通用視圖雖然可以很好地處理簡單情況柱衔,但是超出一些簡單配置選項后就沒有辦法對它們進(jìn)行擴(kuò)展或定制了,這限制了它們在許多現(xiàn)實應(yīng)用程序中的使用愉棱。
創(chuàng)建類通用視圖與創(chuàng)建函數(shù)通用視圖的目的相同(使視圖開發(fā)更容易)唆铐。然而,使用 mixins 來實現(xiàn)的解決方案比采用函數(shù)通用視圖更具擴(kuò)展性和靈活性奔滑。
如果您以前曾嘗試過函數(shù)通用視圖艾岂,并發(fā)現(xiàn)了它們的缺點,那么不應(yīng)該將類通用視圖簡單地看做函數(shù)通用視圖的類方式等效朋其,而應(yīng)該看做是解決通用視圖問題的新方法王浴。
Django 構(gòu)建類通用視圖的基類和 mixins 工具包非常靈活脆炎,它為簡單應(yīng)用可能涉及到的默認(rèn)方法和屬性提供了很多鉤子。例如氓辣,不僅可以通過為 form_class 賦值設(shè)置類屬性秒裕,還可以使用 get_form 方法設(shè)置類屬性,默認(rèn)情況下钞啸,該方法調(diào)用只返回類的 form_class 屬性的 get_form_class 方法几蜻。這為設(shè)置表單提供了從簡單屬性到完全動態(tài)調(diào)用掛接等多種選項。這些選項看起來像增加了簡單情況的復(fù)雜性爽撒,但如果沒有這些選項入蛆,更高級的設(shè)計將受到限制。
使用類視圖
類視圖的核心思想在于使用不同的類實例方法響應(yīng)不同的 HTTP 請求方法硕勿,而不是在單個視圖函數(shù)中使用條件分支代碼哨毁。
視圖函數(shù)處理 HTTP GET 的代碼是這樣的:
from django.http import HttpResponse
def my_view(request):
if request.method == 'GET':
# <view logic>
return HttpResponse('result')
在類視圖中,它將變?yōu)椋?/p>
from django.http import HttpResponse
from django.views import View
class MyView(View):
def get(self, request):
# <view logic>
return HttpResponse('result')
由于 Django 的 URL 解析器希望將請求和關(guān)聯(lián)的參數(shù)發(fā)送到可調(diào)用函數(shù)(而不是類)源武,類視圖定義了一個 as_view() 方法扼褪,該方法為匹配關(guān)聯(lián) URL 模式的請求提供一個可調(diào)用函數(shù)。as_views() 函數(shù)創(chuàng)建一個類實例并調(diào)用該類實例的 dispatch() 方法粱栖。 dispatch 對請求進(jìn)行分析话浇,確定請求類型(GET,POST等)并將請求匹配到對應(yīng)方法(如果已定義對應(yīng)方法)闹究,或引發(fā) HttpResponseNotAllowed 異常(如果沒定義對應(yīng)方法):
# urls.py
from django.conf.urls import url
from myapp.views import MyView
urlpatterns = [
url(r'^about/$', MyView.as_view()),
]
值得注意的是幔崖,類方法返回的內(nèi)容與函數(shù)視圖返回的內(nèi)容相同,都是某種形式的 HttpResponse 渣淤。 這意味著 http快捷函數(shù) 或 TemplateResponse 對象在類視圖中是有效的赏寇。
雖然最小的類視圖不需要設(shè)置任何類屬性就可以實現(xiàn)工作,類屬性在許多類設(shè)計中非常有用价认,我們可以通過兩種方式配置或設(shè)置類屬性:
第一種方法是標(biāo)準(zhǔn)的 Python 方法--在子類中覆蓋屬性和方法嗅定。 如果父類有一個 greeting 屬性:
from django.http import HttpResponse
from django.views import View
class GreetingView(View):
greeting = "Good Day"
def get(self, request):
return HttpResponse(self.greeting)
可以在子類中這樣重寫:
class MorningGreetingView(GreetingView):
greeting = "Morning to ya"
另一種方法是在 URLconf 中的 as_views() 中以關(guān)鍵詞參數(shù)的形式進(jìn)行配置。
urlpatterns = [
url(r'^about/$', GreetingView.as_view(greeting="G'day")),
]
注意:為請求實例化視圖類時用踩,通過 as_view() 入口設(shè)置的類屬性只在定義 URL 時配置一次渠退。
使用mixins
Mixins是一種多繼承的形式,在多繼承中多個父類的行為和屬性可以互相結(jié)合脐彩。
例如碎乃,通用類視圖中有一個名為 TemplateResponseMixin 的 mixin ,它的主要作用是定義render_to_response()方法丁屎。當(dāng)與 View 類的行進(jìn)行結(jié)合時荠锭,結(jié)果是 TemplateView 類。TemplateView 類將請求分配給合適的匹配方法( View 類定義的行為)晨川,并通過輸入 template_name 屬性的 render_to_reponse() 方法返回一個TemplateResponse 對象( TemplateResponseMixin 定義的一個行為)证九。
Mixins 是在多個類中重用代碼的絕佳方法,但是使用它們也需要一些代價共虑。 代碼分散到 mixins 中越多愧怜,閱讀一個子類并知道它能做什么就越困難,當(dāng)創(chuàng)建具有深繼承樹的類的子類時妈拌,也越難知道要重寫哪個 mixin 的哪個方法拥坛。
還要注意,只能繼承一個通用視圖尘分,也就是說猜惋,只有一個父類可以繼承 View ,而其余的(如果有的話)應(yīng)該繼承 mixins 培愁。 嘗試?yán)^承多個繼承 View 的類將無法正常工作著摔,例如,嘗試組合 ProcessFormView 和 ListView 實現(xiàn)在列表頂部使用表單不會得到預(yù)期的結(jié)果定续。
使用類視圖處理表單
處理表單的函數(shù)視圖看起來是這樣的:
from django.http import HttpResponseRedirect
from django.shortcuts import render
from .forms import MyForm
def myview(request):
if request.method == "POST":
form = MyForm(request.POST)
if form.is_valid():
# <process form cleaned data>
return HttpResponseRedirect('/success/')
else:
form = MyForm(initial={'key': 'value'})
return render(request, 'form_template.html', {'form': form})
對應(yīng)的類視圖是這樣的:
from django.http import HttpResponseRedirect
from django.shortcuts import render
from django.views import View
from .forms import MyForm
class MyFormView(View):
form_class = MyForm
initial = {'key': 'value'}
template_name = 'form_template.html'
def get(self, request, *args, **kwargs):
form = self.form_class(initial=self.initial)
return render(request, self.template_name, {'form': form})
def post(self, request, *args, **kwargs):
form = self.form_class(request.POST)
if form.is_valid():
# <process form cleaned data>
return HttpResponseRedirect('/success/')
return render(request, self.template_name, {'form': form})
這是一個非常簡單的例子谍咆,但是仍然可以看到,我們可以通過重寫類屬性(比如form_class)來自定義視圖私股,重寫類屬性的方法包括 URLconf 配置摹察、創(chuàng)建子類并重寫一個或多個方法(或者兩者都有)。
裝飾類視圖
不僅可以通過 mixins 擴(kuò)展類視圖倡鲸,還可以使用裝飾器供嚎。由于類視圖不是函數(shù),對 as_view() 進(jìn)行裝飾與對創(chuàng)建的子類進(jìn)行裝飾的工作方式不同峭状。
在URLconf中進(jìn)行裝飾
裝飾類視圖最簡單的方法是裝飾 as_view() 方法的結(jié)果克滴,最簡單的實現(xiàn)方法是在部署視圖的 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())),
]
這種方法對使用的實例進(jìn)行裝飾宁炫。 如果希望每個視圖的實例都被裝飾偿曙,需要采取不同的方法。
裝飾類
如果需要裝飾類視圖的每個實例羔巢,則需要對類進(jìn)行裝飾望忆。可用通過裝飾類的 dispatch() 方法來實現(xiàn)對類進(jìn)行裝飾竿秆。類方法與單獨函數(shù)不盡相同启摄,因此不能只對類方法應(yīng)用一個函數(shù)裝飾器,而需要先將其轉(zhuǎn)換為一個方法裝飾器幽钢。method_decorator 裝飾器可以將一個函數(shù)裝飾器轉(zhuǎn)換為一個方法裝飾器歉备,以使函數(shù)裝飾器可以用于實例方法中,例如:
from django.contrib.auth.decorators import login_required
from django.utils.decorators import method_decorator
from django.views.generic import TemplateView
class ProtectedView(TemplateView):
template_name = 'secret.html'
@method_decorator(login_required)
def dispatch(self, *args, **kwargs):
return super(ProtectedView, self).dispatch(*args, **kwargs)
或者匪燕,可以更簡潔地通過裝飾類并將要裝飾的方法的名稱作為關(guān)鍵字參數(shù)進(jìn)行傳遞:
@method_decorator(login_required, name='dispatch')
class ProtectedView(TemplateView):
template_name = 'secret.html'
如果有一個在幾個位置使用的通用裝飾器集合蕾羊,則可以定義一個裝飾器列表或元組來進(jìn)行裝飾喧笔,而不用多次執(zhí)行method_decorator() 。下面例子中的兩個類是等效的:
decorators = [never_cache, login_required]
@method_decorator(decorators, name='dispatch')
class ProtectedView(TemplateView):
template_name = 'secret.html'
@method_decorator(never_cache, name='dispatch')
@method_decorator(login_required, name='dispatch')
class ProtectedView(TemplateView):
template_name = 'secret.html'
裝飾器將按照傳入到裝飾器的順序?qū)φ埱筮M(jìn)行處理龟再,上例中书闸,never_cache() 將比 login_required() 先處理請求。
在這個例子中利凑,每個 ProtectedView 的實例都將具有登錄保護(hù)浆劲。
注意:method_decorator 將
*args
和**kwargs
作為參數(shù)傳輸給類的裝飾方法。如果方法不接收兼容參數(shù)則可能會引發(fā) TypeError 異常哀澈。