1. 程式人生 > >Django 通用類視圖

Django 通用類視圖

.sh 而不是 比較 回車 一次 get請求 span pro 動態

引文

所有的類視圖都繼承django.views.generic.base.View類。

在URLconf中簡單的使用通用視圖

如果只是簡單的做一些屬性修改,可以使用as_view()方法,如下所示:

from django.urls import path
from django.views.generic import TemplateView

urlpatterns = [
    path(about/, TemplateView.as_view(template_name="about.html")),
]

繼承通用視圖

繼承是最強大和有效的使用方式,如下:

# some_app/views.py
from django.views.generic import TemplateView class AboutView(TemplateView): template_name = "about.html"

然後就可以像下面一樣使用這個子類了。註意,由於類不是函數,所以需要使用as_view()這個類方法將一個基於類的視圖轉換成函數形式的接口。

# urls.py
from django.urls import path
from some_app.views import AboutView

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

一個訪問圖書館最新一本書的例子

路由:

from django.urls import path
from books.views import BookListView

urlpatterns = [
    path(books/, BookListView.as_view()),
]

視圖:

from django.http import HttpResponse
from django.views.generic import ListView
from books.models import Book

class BookListView(ListView):
    model 
= Book def head(self, *args, **kwargs): last_book = self.get_queryset().latest(publication_date) response = HttpResponse(‘‘) # RFC 1123 date format response[Last-Modified] = last_book.publication_date.strftime(%a, %d %b %Y %H:%M:%S GMT) return response

這個例子中,如果用戶通過GET請求數據,那麽將正常的返回響應數據。而如果通過HEAD請求,將使用我們寫的head方法中的業務邏輯。

基於類的視圖和基於函數的視圖比較:

  • 通過HTTP請求方法的不同,將代碼分隔在不同的類方法中,比如GET和POST,而不是類函數中的條件判斷;
  • 可以使用面向對象的技巧,比如混入
  • 兩者沒有絕對的好壞和壓倒性優勢之分

早期,人們在視圖開發中發現了一些常見的習語和句式,也就是重復性代碼和工作。於是引入了基於函數的通用視圖來抽象這些模式,便於一般情況下的視圖開發。

基於函數的通用視圖的問題在於,盡管它們覆蓋了簡單的情況,但是除了一些簡單的配置選項之外,沒有辦法擴展或定制它們,限制了它們在許多現實應用中的實用性。

基於類的通用視圖與基於函數的通用視圖的目標相同,都是想讓視圖開發更容易。由於類視圖可以使用MIXIN等一些面向對象的方法和工具包,使得基於類的通用視圖比基於函數的通用視圖更具擴展性和靈活性。

Django用來構建基於類的通用視圖的基類和混合程序工具包是為了最大程度的靈活性而構建的,因此在默認方法實現和屬性中有許多鉤子,在最簡單的用例中,這些方法是不太可能涉及的。Django給出了幾種指定使用什麽形式、從簡單屬性到完全動態可調用鉤子的選項。對於簡單的情況,這些選擇似乎增加了復雜性,但沒有它們,更先進的設計將受到限制。

使用基於類的視圖

在一個函數視圖中,處理一個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安利給我們的,不是Python的原生用法。

每個類視圖都有一個as_view()方法,用於在urlconf中使用。這個方法會創建一個類視圖的實例,並調用它的dispatch()方法。dispatch方法會在類中查找類似GET\POST之類的類方法,然後與請求request中的HTTP方法匹配。匹配上了就調用對應的代碼,匹配不上就彈出異常HttpResponseNotAllowed。

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

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

至於return返回的什麽,和函數視圖是一樣樣的。

要重寫或者覆蓋一個類屬性有兩種辦法:

一是繼承父類,在子類中重寫父類的屬性,如下所示:

父類:

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"

註意其中的greeting類屬性。

另一種就是簡單粗暴的在URLConf路由條目中修改as_view()方法的參數。當然,參數名必須是存在的類屬性,你不能隨便瞎寫!如下所示:

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

但是這種方式有很大的弊端,那就是雖然每次匹配上了url,你的類視圖都會被實例化一次,但是你的URLs卻是在被導入的時候才配置一次,也就是as_view()方法的參數的配置只有一次。也就是說這麽做,就不能再改了!所以,不要偷懶,使用子類吧。

使用Mixin混入

(本站相關鏈接:https://www.cnblogs.com/wcwnina/p/9248162.html)

混入是一種多父類繼承的形式,其基礎理論知識請參考我的Python教程中多繼承相關章節。

Mixin是跨多個類重用代碼的一個很好的方法,但是它們會帶來一些代價。你的代碼散布在MIXIN中越多,閱讀子類就越難,很難知道它到底在做什麽。如果你在對具有多級繼承樹的子類進行分類,就更難以判斷子類的方法到底繼承的是哪個先祖,俗稱‘家譜亂了’。

需要註意的是你的父類中只有一個類可以繼承最頂級的View類,其它的必須以Mixin方法混入。

使用類視圖處理表單

一個用來處理表單的函數視圖通常是下面這樣的:

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})

看起來類視圖好像比函數視圖代碼多了很多,復雜了一些,貌似沒啥優點啊。但是如果你有多個類似的視圖需要編寫,那麽你就可以發揮子類的繼承和復寫神操作了,分分鐘整出個新的視圖來。或者直接在URLConf中修改參數!或者兩種操作同時使用!

其實,到現在你應該明白了,類視圖適用於大量重復性的視圖編寫工作,在簡單的場景下,沒幾個視圖需要編寫,或者各個視圖差別很大的情況時,還是函數視圖更有效!所以,不要認為類視圖是多麽高大上的東西,人雲亦雲!

裝飾類視圖

(本站相關鏈接:https://www.cnblogs.com/wcwnina/p/9248162.html)

除了Mixin,還可以使用裝飾器擴展類視圖。裝飾器的作用取決於你是在創建子類還是使用as_view()。

用法一,在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())),
]

上面怎麽看都像是函數嵌套調用和鏈式調用。

同樣的,這種方式也是在第一次初始化URL配置時才有效,不能做到每來一個請求都有效。或者使用方法二如下:

用法二,在類視圖中裝飾:

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)

註意:

  • 上面要把裝飾器用在dispatch這個方法上,才能在每次請求到達URL時,實例化類視圖時都運行這個裝飾器的功能。
  • 不是每個裝飾器都能運用在類方法上,需要使用method_decorator這個裝飾器的裝飾器方法將裝飾器運用在類方法上。感覺很繞?其實就是說,我們有很多很多的裝飾器,但其中有一些不能直接裝飾dispatch這種類方法。那怎麽辦呢?套層殼!用method_decorator裝飾器包裹起來,假裝成一個能用的。

有時候,簡單地用一下,可以寫成下面地精簡版:

@method_decorator(login_required, 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

為了偷懶,我們可以這麽做:

decorators = [never_cache, login_required]

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

到底哪個好點?感覺都一樣啊!除非你同時有幾十上百個裝飾器需要使用!但這可能麽?

唯一需要註意地是裝飾器是有先後順序的。上面的例子中,never_cache就要先於login_required被調用。

最後,偷偷的告訴你使用method_decorator有時會導致TypeError異常,因為參數傳遞的原因。當然,一般人碰不到的,碰得到的人都不一般,都能自己查Django官方文檔掌握問題原因。

  至此。轉載請註明出處。

[參考:http://www.liujiangblog.com]

Django 通用類視圖