剛開始的時候,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
轉載請注明出處