Django4.0 基于類的視圖-使用

2022-03-16 17:57 更新

基于類的視圖提供另一種將視圖實現(xiàn)為 Python 對象而不是函數(shù)的方法。它們不能替代基于函數(shù)的視圖,但與基于函數(shù)的視圖相比,它們是有某些不同和優(yōu)勢的。

  • 與特定的 HTTP 方法(?GET?, ?POST?等)關聯(lián)的代碼組織能通過單獨的方法替代條件分支來解決。
  • 面向?qū)ο蠹夹g(比如 ?mixins ?多重繼承)可用于將代碼分解為可重用組件。

通用視圖、基于類的視圖和基于類的通用視圖的關系和歷史

一開始,這里只有視圖函數(shù),Django 傳遞 ?HttpRequest ?函數(shù)并預期返回一個 ?HttpResponse ?。這是 Django 能提供的范圍。
早期人們就發(fā)現(xiàn)在視圖開發(fā)過程中有常見的約定和模式。引入了基于函數(shù)的通用視圖為這些常見情況抽象這些模式和簡單視圖的開發(fā)。
基于函數(shù)的通用視圖的問題是即便它們可以很好的處理簡單案例,但除了一些配置選項之外,沒辦法擴展或自定義它們,這樣就限制了它們在實際應用中用途。
創(chuàng)建基于類的通用視圖與基于函數(shù)的通用視圖具有相同的目標,那就是使視圖開發(fā)更容易。然而,通過使用 mixins 實現(xiàn)解決方案的方式提供了一個工具包,使基于類的通用視圖比基于函數(shù)的通用視圖更靈活,更有擴展性。
如果你之前有嘗試過基于函數(shù)的通用視圖并發(fā)現(xiàn)了它的不足之處,那么你不應該認為基于類的通用視圖只是基于類的等效視圖,而是作為一種新的方法來解決通用視圖要解決的原始問題。
為了獲得最大的靈活性,Django 使用基礎類和?mixins?的工具包來構建通用視圖,因此在默認方法實現(xiàn)和屬性的形式中有很多鉤子,你在最簡單的用例中不太可能涉及到這些鉤子。比如,不要將你限制為 ?form_class ?的基于類的屬性,使用 ?get_form ?方法來實現(xiàn),使用 ?get_form ?方法,它調(diào)用 ?get_form_class ?方法,在默認實現(xiàn)里只返回類的 ?form_class ?屬性。這給你一些選項來指定使用的表單,從簡單屬性到完全動態(tài)的可調(diào)用屬性。這些選項看起來增加了復雜度,但沒有它們,會限制更高級的設計。

使用基于類的視圖

本質(zhì)上來說,基于類的視圖允許你使用不同的類實例方法響應不同 HTTP 請求方法,而不是在單個視圖函數(shù)里使用有條件分支的代碼。
因此在視圖函數(shù)里處理 HTTP ?GET ?的代碼應該像下面這樣:

from django.http import HttpResponse

def my_view(request):
    if request.method == 'GET':
        # <view logic>
        return HttpResponse('result')

而在基于類的視圖里,會變成:

from django.http import HttpResponse
from django.views import View

class MyView(View):
    def get(self, request):
        # <view logic>
        return HttpResponse('result')

因為 Django 的 URL 解析器期望發(fā)送請求和相關參數(shù)來調(diào)動函數(shù)而不是類,基于類的視圖有一個 ?as_view()? 類方法,當一個請求到達的 URL 被關聯(lián)模式匹配時,這個類方法返回一個函數(shù)。這個函數(shù)創(chuàng)建一個類的實例,調(diào)用 ?setup()? 初始化它的屬性,然后調(diào)用 ?dispatch()? 方法。 ?dispatch ?觀察請求并決定它是 ?GET ?和 ?POST?,等等。如果它被定義,那么依靠請求來匹配方法,否則會引發(fā) ?HttpResponseNotAllowed ?。

# urls.py
from django.urls import path
from myapp.views import MyView

urlpatterns = [
    path('about/', MyView.as_view()),
]

值得注意的是,你的方法返回值和基于函數(shù)的視圖返回值是相同的,既某種形式的 ?HttpResponse ?。這意味著 http 快捷函數(shù) 或 ?TemplateResponse ?對象可以使用基于類里的視圖。
雖然基于類的最小視圖不需要任何類屬性來執(zhí)行任務,類屬性在很多基于類的始終很常見,這里有兩種方法來配置或設置類屬性。
第一種是子類化標準 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 中將配置類屬性作為參數(shù)來調(diào)用 ?as_view()

urlpatterns = [
    path('about/', GreetingView.as_view(greeting="G'day")),
]

注解:當你的類為發(fā)送給它的每個請求實例化時,通過 ?as_view()? 入口點設置的類屬性在導入 URLs 的時候只配置一次。

使用mixins

?Mixins ?是一個多繼承表單,其中可組合多個父類的行為和屬性。
舉例,在通用基于類的視圖中,名為 ?TemplateResponseMixin ?的 ?mixin ?的首要目的是定義方法 ?render_to_response()?。當與視圖的基類行為結合使用時,結果是一個 ?TemplateView ?類,它將請求分派到適當?shù)钠ヅ浞椒ǎㄔ谝晥D基類中定義的行為),并且具有 ?render_to_response()? 方法,該方法使用 ?template_name ?屬性返回一個 ?TemplateResponse ?對象(在 ?TemplateResponseMixin ?中定義的行為)。
?Mixins ?是在多個類中重用代碼的絕佳方法,但它們需要一些代價。代碼分散在 ?Mixins ?中的越多,理解子類并知道它到底在做什么就越困難,而且如果你正在子類化具有深繼承樹的東西,那么就越難知道要從哪個 ?mixns ?的方法中來覆蓋它。
也需要注意你只能從一個通用視圖繼承——只有一個父類可以繼承自 ?View ?,剩余的(如果有的話)應該繼承自 ?mixins ?。試著從更多的繼承自 ?View ?的類繼承的話——例如試著在列表頂部使用表單并組合 ?ListView ?——將無法按照預期工作。

使用基于類的視圖處理表單

處理表單的基于函數(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})

類似的基于類的視圖可能看起來像這樣:

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 ?配置或者子類化和重寫一個或多個方法(或者兩種都可以)。

裝飾基于類的視圖

基于類的視圖的擴展不僅限于使用 ?mixins ?,你也可以使用裝飾器。因為基于類的視圖不是函數(shù),所以根據(jù)你是使用 ?as_view()? 還是創(chuàng)建子類,裝飾它們的工作方式會有不同。

在 URLconf 中裝飾

可以通過裝飾 ?as_view()? 方法的結果來調(diào)整基于類的視圖。最簡單的方法是在你部署視圖的 ?URLconf ?中執(zhí)行此操作:

from django.contrib.auth.decorators import login_required, permission_required
from django.views.generic import TemplateView

from .views import VoteView

urlpatterns = [
    path('about/', login_required(TemplateView.as_view(template_name="secret.html"))),
    path('vote/', permission_required('polls.can_vote')(VoteView.as_view())),
]

這個方式在每個基本實例上應用裝飾器。如果你想裝飾視圖的每個實例,你需要采用不同方式。

裝飾類

裝飾基于類的視圖的每個實例,你需要裝飾類定義本身。為此,你可以將裝飾器應用到類的 ?dispatch()? 方法。
類上的方法與獨立函數(shù)完全不同,因此你不能應用函數(shù)裝飾器到方法上——你需要先將它轉(zhuǎn)換為方法裝飾器。?method_decorator? 裝飾器轉(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().dispatch(*args, **kwargs)

或者,更簡潔的說,你可以用裝飾類來代替,并作為關鍵參數(shù) ?name ?傳遞要被裝飾的方法名:

@method_decorator(login_required, name='dispatch')
class ProtectedView(TemplateView):
    template_name = 'secret.html'

如果你在一些地方使用了常見的裝飾器,你可以定義一個裝飾器列表或元組,并使用它而不是多次調(diào)用 ?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'

裝飾器將按照它們傳遞給裝飾器的順序來處理請求。在這個例子里,?never_cache()? 將在 ?login_required()? 之前處理請求。
在這個例子里,?ProtectedView ?的每一個實例將被登錄保護。盡管這些例子使用 ?login_required? ,但可以使用 ?LoginRequiredMixin ?獲得同樣的行為。

注解:?method_decorator ?將? *args? 和 ?**kwargs? 作為參數(shù)傳遞給類上的裝飾方法。如果你的方法不接受兼容參數(shù)集合,它會引發(fā) ?TypeError ?錯誤。


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號