基于類的視圖簡介

2021-10-19 19:31 更新

基于類的視圖簡介

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

  • 與特定HTTP方法(GET,POST等)相關(guān)的代碼組織可以通過單獨的方法而不是條件分支來解決。
  • 諸如mixin(多重繼承)之類的面向?qū)ο蠹夹g(shù)可用于將代碼分解為可重用的組件。

通用視圖,基于類的視圖和基于類的通用視圖的關(guān)系和歷史記錄

開始時只有視圖函數(shù)協(xié)定,Django將您的函數(shù)傳遞給,HttpRequest并期望將 傳遞給HttpResponse。這就是Django提供的功能。

早期就認識到在視圖開發(fā)中發(fā)現(xiàn)了常見的習(xí)慣用法和模式。引入了基于函數(shù)的通用視圖,以抽象化這些模式并簡化常見情況下的視圖開發(fā)。

基于函數(shù)的通用視圖的問題在于,盡管它們很好地涵蓋了簡單的情況,但無法擴展或自定義某些配置選項之外的視圖,從而限制了它們在許多實際應(yīng)用程序中的用途。

創(chuàng)建基于類的通用視圖的目的與基于函數(shù)的通用視圖相同,以使視圖開發(fā)更加容易。但是,通過使用mixins來實現(xiàn)解決方案的方式提供了一個工具包,該工具包使得基于類的通用視圖比基于功能的對應(yīng)視圖更具可擴展性和靈活性。

如果您過去曾經(jīng)嘗試過基于函數(shù)的通用視圖,但發(fā)現(xiàn)缺少這些功能,則不應(yīng)將基于類的通用視圖視為基于類的等效視圖,而應(yīng)將其視為解決通用視圖旨在解決的原始問題的全新方法。解決。

Django用于構(gòu)建基于類的泛型視圖的基類和mixin工具包的構(gòu)建具有最大的靈活性,因此,它們具有默認方法實現(xiàn)和屬性形式的許多鉤子,您可能不會在最簡單的用法中關(guān)注它們案件。例如,實現(xiàn)不是form_class使用get_form方法的基于類的屬性,而是使用了一種方法,該get_form_class方法調(diào)用一種方法,該方法在其默認實現(xiàn)中返回form_class類的屬性。這為您提供了幾個選項,用于指定從屬性到完全動態(tài),可調(diào)用的鉤子使用哪種形式。對于簡單情況,這些選項似乎增加了空心的復(fù)雜性,但是如果沒有這些選項,則會限制更高級的設(shè)計。

使用基于類的意見

從本質(zhì)上講,基于類的視圖使您可以使用不同的類實例方法來響應(yīng)不同的HTTP請求方法,而不是使用單個視圖函數(shù)中的有條件分支代碼。

因此,GET在視圖函數(shù)中用于處理HTTP的代碼如下所示:

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()class方法,該類方法返回一個函數(shù),該請求可以在請求到達與關(guān)聯(lián)模式匹配的URL時被調(diào)用。該函數(shù)創(chuàng)建該類的實例,調(diào)用 setup()以初始化其屬性,然后調(diào)用其dispatch()方法。 dispatch查看該請求以確定它是否為GET, POST等,并將請求轉(zhuǎn)發(fā)給匹配的方法(如果已定義),否則將其引發(fā)HttpResponseNotAllowed:

# urls.py
from django.urls import path
from myapp.views import MyView
?
urlpatterns = [
  path('about/', MyView.as_view()),
]

值得注意的是,您的方法返回的內(nèi)容與您從基于函數(shù)的視圖返回的內(nèi)容相同,即的某種形式 HttpResponse。這意味著 http快捷方式或 TemplateResponse對象可在基于類的視圖中有效使用。

盡管最小的基于類的視圖不需要任何類屬性即可執(zhí)行其工作,但是類屬性在許多基于類的設(shè)計中很有用,并且有兩種配置或設(shè)置類屬性的方法。

第一種是子類化和覆蓋子類中的屬性和方法的標準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"

另一個選擇是將類屬性配置為as_view()URLconf中的調(diào)用的關(guān)鍵字參數(shù) :

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

注意:在為分配給它的每個請求實例化您的類時,通過as_view()導(dǎo)入點設(shè)置的類屬性 在導(dǎo)入URL時僅配置一次。

使用混入

Mixins是多重繼承的一種形式,可以將多個父類的行為和屬性進行組合。

例如,在基于通用類的視圖中,有一個mixin, TemplateResponseMixin其主要目的是定義method render_to_response()。當與View 基類的行為組合時,結(jié)果是一個TemplateView 類,該類會將請求分派到適當?shù)钠ヅ浞椒ǎ╒iew基類中定義的行為),并且具有 render_to_response() 使用 template_name 屬性返回TemplateResponse 對象(行為)的方法。 )中定義TemplateResponseMixin。

Mixins是在多個類之間重用代碼的絕佳方法,但是它們會帶來一些成本。您的代碼散布在mixin中的次數(shù)越多,讀取子類并了解其確切操作的難度就越大,而如果您正在子類化具有深層繼承樹。

還要注意,您只能從一個通用視圖繼承-也就是說,只有一個父類可以繼承,View其余(如果有)應(yīng)該是mixins。嘗試從多個繼承的類中進行繼承View-例如,嘗試使用列表頂部的表單并組合ProcessFormView和 ListView-將無法按預(yù)期工作。

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

處理表單的基于函數(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配置,或子類化并覆蓋一個或多個方法(或兩者)!)來定制此視圖 。 。

裝飾基于類的視圖

基于類的視圖的擴展不僅限于使用混合。您也可以使用裝飾器。由于基于類的視圖不是函數(shù),因此根據(jù)您正在使用as_view()還是創(chuàng)建子類來裝飾它們的工作方式有所不同。

在URLconf中裝飾

您可以通過裝飾as_view()方法的結(jié)果來調(diào)整基于類的視圖 。最簡單的方法是在部署視圖的URLconf中:

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())),
]

此方法基于每個實例應(yīng)用裝飾器。如果要裝飾視圖的每個實例,則需要采用其他方法。

裝飾類

要修飾基于類的視圖的每個實例,您需要修飾類定義本身。為此,您可以將裝飾器應(yīng)用于dispatch()類的 方法。

類上的方法與獨立函數(shù)并不完全相同,因此您不能僅將函數(shù)裝飾器應(yīng)用于該方法–您需要首先將其轉(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)

或者,更簡潔地說,您可以代替裝飾類,并將要裝飾的方法的名稱作為關(guān)鍵字參數(shù)傳遞name:

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

如果您在多個地方使用了一組通用裝飾器,則可以定義一個裝飾器列表或元組,然后使用它而不是method_decorator()多次調(diào)用 。這兩個類是等效的:

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ù)傳遞給類中經(jīng)過修飾的方法。如果您的方法不接受一組兼容的參數(shù),它將引發(fā) TypeError異常。

詳情參考: https://docs.djangoproject.com/en/3.0/


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

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號