Django 類視圖(二)類視圖介紹

本文是對 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 異常哀澈。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末牌借,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子割按,更是在濱河造成了極大的恐慌膨报,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,406評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件哲虾,死亡現(xiàn)場離奇詭異丙躏,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)束凑,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,395評論 3 398
  • 文/潘曉璐 我一進(jìn)店門晒旅,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人汪诉,你說我怎么就攤上這事废恋。” “怎么了扒寄?”我有些...
    開封第一講書人閱讀 167,815評論 0 360
  • 文/不壞的土叔 我叫張陵鱼鼓,是天一觀的道長。 經(jīng)常有香客問我该编,道長迄本,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,537評論 1 296
  • 正文 為了忘掉前任课竣,我火速辦了婚禮嘉赎,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘于樟。我一直安慰自己公条,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 68,536評論 6 397
  • 文/花漫 我一把揭開白布迂曲。 她就那樣靜靜地躺著靶橱,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上关霸,一...
    開封第一講書人閱讀 52,184評論 1 308
  • 那天传黄,我揣著相機(jī)與錄音,去河邊找鬼谒拴。 笑死尝江,一個胖子當(dāng)著我的面吹牛涉波,可吹牛的內(nèi)容都是我干的英上。 我是一名探鬼主播,決...
    沈念sama閱讀 40,776評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼啤覆,長吁一口氣:“原來是場噩夢啊……” “哼苍日!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起窗声,我...
    開封第一講書人閱讀 39,668評論 0 276
  • 序言:老撾萬榮一對情侶失蹤相恃,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后笨觅,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體拦耐,經(jīng)...
    沈念sama閱讀 46,212評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,299評論 3 340
  • 正文 我和宋清朗相戀三年见剩,在試婚紗的時候發(fā)現(xiàn)自己被綠了杀糯。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,438評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡苍苞,死狀恐怖固翰,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情羹呵,我是刑警寧澤骂际,帶...
    沈念sama閱讀 36,128評論 5 349
  • 正文 年R本政府宣布,位于F島的核電站冈欢,受9級特大地震影響歉铝,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,807評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望器躏。 院中可真熱鬧种冬,春花似錦、人聲如沸掸宛。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,279評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽呀非。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間岸裙,已是汗流浹背猖败。 一陣腳步聲響...
    開封第一講書人閱讀 33,395評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留降允,地道東北人恩闻。 一個月前我還...
    沈念sama閱讀 48,827評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像剧董,于是被迫代替她去往敵國和親幢尚。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,446評論 2 359

推薦閱讀更多精彩內(nèi)容

  • 基于類的視圖 Django中的視圖是一個可調(diào)用對象翅楼,它接收一個請求然后返回一個響應(yīng)尉剩。這個可調(diào)用對象不僅僅限于函數(shù),...
    蘭山小亭閱讀 4,581評論 1 13
  • Django基于類的視圖 1.基于類的視圖簡介 基于類的視圖使用Python 對象實現(xiàn)視圖毅臊,它提供除函數(shù)視圖之外的...
    常大鵬閱讀 8,650評論 0 25
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理理茎,服務(wù)發(fā)現(xiàn),斷路器管嬉,智...
    卡卡羅2017閱讀 134,695評論 18 139
  • 10 構(gòu)建一個在線學(xué)習(xí)平臺 10.5 創(chuàng)建內(nèi)容管理系統(tǒng) 現(xiàn)在我們已經(jīng)創(chuàng)建了一個萬能的數(shù)據(jù)模型皂林,接下來我們會創(chuàng)建一個...
    lakerszhy閱讀 1,608評論 0 4
  • 昨天半夜,女兒左翻翻右翻翻自己醒了蚯撩。我也被她折騰醒了础倍,抬眼看她,昏暗的光線下求厕,她用小手撐著床顫顫巍巍地站了...
    ddb44dc2b8cc閱讀 361評論 0 0