介紹——基於類的視圖(class-based view)


​剛開始的時候,django只有基於函數的視圖(Function-based views)。為了解決開發視圖中繁雜的重復代碼,基於函數的通用視圖( Class-based generic views)出現了,但是不久它的弊端就顯示出來:無法擴展、無法定制。基於函數的通用視圖的不靈活導致它在現實世界中的應用受限。基於類的通用視圖也是出於同樣的目的被開發出來,它提供一個工具箱並支持多重繼承,隨着它的應用,人們發現它的可擴展性和靈活性遠超它的小兄弟——基於函數的通用視圖。

基於類的通用視圖是基於函數的通用視圖的質的飛躍,而不僅僅是改進

 

使用基於類的視圖

一般而言,如果要對不同的HTTP請求做出不同的相應的話,function-based views會在單一的函數中采用判斷分支的方法,比如:

 

1
2
3
4
5
6
7
8
9
from   django.http  import   HttpResponse
  
def   my_view(request):
     if   request.method  = =   'GET' :
         # <view logic>
         return   HttpResponse( 'result' )
     if   request.method  = =   'POST' :
         # <view logic>
         return   HttpResponse( 'result' )

 

而在class-based views中,你可以用不同的類實例的方法來響應不同的HTTP request,如:

1
2
3
4
5
6
7
8
9
10
from   django.http  import   HttpResponse
from   django.views.generic.base  import   View
  
class   MyView(View):
     def   get( self , request):
         # <view logic>
         return   HttpResponse( 'result' )
     def   post( self , request):
         # <view logic>
         return   HttpResponse( 'result' )

 

django的URL解析器需要將request和相應的參數傳遞給一個可調用的函數,而不是一個類。所以class-based view提供一個類方法:as_view()來解決這個問題,as_view()方法讓你可以把類當做函數來調用。as_view創建一個類實例,然后調用它的dispatch方法,dispatch分析出request是GET、POST或者其他,然后將request匹配給相應的函數,比如將POST請求匹配給post()函數,如果給函數沒有定義的話,將引發HttpResponseNotAllowed錯誤。

 

1
2
3
4
5
6
7
# urls.py
from   django.conf.urls  import   patterns
from   myapp.views  import   MyView
 
urlpatterns  =   patterns('',
     (r '^about/' , MyView.as_view()),
)

 

雖然小型的class-based view並不需要依靠類屬性來完成它的工作,但是類屬性在很多的基於類的設計中都很有用。設置類屬性有兩個方法。

第一個方法是標准的python方法:在子類中重寫類的屬性和方法,比如

1
2
3
4
5
6
7
8
from   django.http  import   HttpResponse
from   django.views.generic.base  import   View
 
class   GreetingView(View):
     greeting  =   "Good Day"
 
     def   get( self , request):
         return   HttpResponse( self .greeting)

你可以在子類中這樣重寫:

1
2
class   MorningGreetingView(GreetingView):
     greeting  =   "Morning to ya"

第二個方法是在URLconf中將類屬性作為參數傳遞給as_view():

1
2
3
urlpatterns  =   patterns('',
     (r '^about/' , GreetingView.as_view(greeting = "G'day" )),
)

 

使用mixins

mixin是多重繼承的一種,它將父類的行為和屬性結合在一起。比如說,在基於類的通用視圖中,有一個mixin叫TemplateResponseMinxin,它原本的目的是定義方法render_to_response()。當與基礎類View的行為結合時,結果是一個神奇的TemplateView類:它擁有分析request並作出相應匹配的方法(原本定義在View中的行為),也擁有一個接受一個template_name並返回一個TempalteReponse對象的render_to_response()方法(原本定義在 TemplateResponseMixin中的行為) 

 

mixins是在不同的類之間重用代碼的出色方法,但是它也帶來了一些代價。也許你已經注意到了,如果你濫用這種方法的話,你將會迷失在mixin中,因為在冗長的繼承樹中,你很難辨清一個子類到底是用來干嘛的。

 

注意你只能從一個通用視圖中繼承,就是說,只能有一個父類是從View類繼承來的。如果你在多個View類的子類中繼承,比如嘗試將ProsessFormView和ListView結合,結果將不會是你期待的那樣。

 

用基於類的視圖處理表格

一個基於函數的視圖在處理表格時,看起來會像這樣:

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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})

而相似的基於類的視圖會想這樣:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from   django.http  import   HttpResponseRedirect
from   django.shortcuts  import   render
from   django.views.generic.base  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配置重寫類屬性(像form_class),或者重寫、繼承一個或更多的方法。

 

裝飾基於類的視圖

class-based view的擴展並不局限於mixins,你也可以使用裝飾器。由於基於類的視圖不是函數,使用as_view或者創建子類將會以不同的方式了來裝飾他們:

 

1
2
3
4
5
6
7
8
9
from   django.contrib.auth.decorators  import   login_required, permission_required
from   django.views.generic  import   TemplateView
 
from   .views  import   VoteView
 
urlpatterns  =   patterns('',
     (r '^about/' , login_required(TemplateView.as_view(template_name = "secret.html" ))),
     (r '^vote/' , permission_required( 'polls.can_vote' )(VoteView.as_view())),
)

這是裝飾一個實例的方法。如果你想裝飾視圖的每一個實例,你需要使用另一種方法。

 

裝飾類

為了裝飾基於類的視圖的每一個實例,你需要裝飾這個類本身。你可以將這個裝飾器應用於類的dispatch()方法來達到這一目的。

類的方法和獨立的方法並不完全一樣,所以你不能直接將函數裝飾器應用於類方法——你需要先將它轉化成一個方法裝飾器。method_decorator裝飾器將一個函數裝飾器轉化成一個方法裝飾器,這樣一來,他就可以應用於實例的方法。例如:

1
2
3
4
5
6
7
8
9
10
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)

在這個例子中,每個ProtecedView都將有login保護。

注意,method_decorator將 *args 和 **kwargs傳遞給被裝飾的類方法做參數。如果你的方法不接受這種參數,它將引發TypeError

 

轉載請注明出處


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM