Views,URLconf的advanced用法
之前有介紹了一些views和路徑匹配的基礎用法,在這里介紹一些關於它們的advanced用法。
URLconf技巧
因為urls.py也是一個python文件,所以你可以在這個文件中使用python允許的任何語法。
先看之前介紹的例子:
from django.conf.urls
import patterns, include, url
from books.views import hello, search_form, search, contact, thanks
urlpatterns = patterns( '',
url(r '^hello/$', hello),
url(r '^search/$', search),
url(r '^contact/$', contact),
url(r '^contact/thanks/$', thanks),
)
每一個路徑匹配需要導入相應的模塊,所以當app的規范越來越大的時候,第二行的import語句
from books.views import hello, search_form, search, contact, thanks
urlpatterns = patterns( '',
url(r '^hello/$', hello),
url(r '^search/$', search),
url(r '^contact/$', contact),
url(r '^contact/thanks/$', thanks),
)
就越長。。。所以可以采用以下幾種方法來必定:
1. 只import到模塊就可以了,不用寫出函數
from django.conf.urls
import patterns, include, url
from books import views
urlpatterns = patterns( '',
url(r '^hello/$', views.hello),
url(r '^search/$', views. search),
url(r '^contact/$', views.contact),
url(r '^contact/thanks/$', views.thanks),
)
2. 使用字符串來代表需要執行的views函數,這里就需要寫全路徑
from books import views
urlpatterns = patterns( '',
url(r '^hello/$', views.hello),
url(r '^search/$', views. search),
url(r '^contact/$', views.contact),
url(r '^contact/thanks/$', views.thanks),
)
from django.conf.urls
import patterns, include, url
urlpatterns = patterns( '',
url(r '^hello/$', 'books.views.hello'),
url(r '^search/$', 'books.views.search'),
url(r '^contact/$', 'books.views.contact'),
url(r '^contact/thanks/$', 'books.views.thanks'),
)
3. 更簡單的字符串寫法,需要使用到patterns()的第一個參數
urlpatterns = patterns( '',
url(r '^hello/$', 'books.views.hello'),
url(r '^search/$', 'books.views.search'),
url(r '^contact/$', 'books.views.contact'),
url(r '^contact/thanks/$', 'books.views.thanks'),
)
from django.conf.urls
import patterns, include, url
urlpatterns = patterns( 'books.views',
url(r '^hello/$', 'hello'),
url(r '^search/$', 'search'),
url(r '^contact/$', 'contact'),
url(r '^contact/thanks/$', ' thanks'),
)
以上的方法都是合法的,具體看你選擇了。
urlpatterns = patterns( 'books.views',
url(r '^hello/$', 'hello'),
url(r '^search/$', 'search'),
url(r '^contact/$', 'contact'),
url(r '^contact/thanks/$', ' thanks'),
)
因為patterns()返回的對象可以進行加法,當遇到多個views模塊的路徑前綴時,可以使用如下方式
來統一管理你的路徑匹配:
from django.conf.urls
import patterns, include, url
urlpatterns = patterns( 'books.views',
url(r '^hello/$', 'hello'),
url(r '^search/$', 'search'),
url(r '^contact/$', 'contact'),
url(r '^contact/thanks/$', ' thanks'),
)
urlpatterns += patterns( 'weblogs.views',
url(r '^hello/$', 'world'),
)
urlpatterns = patterns( 'books.views',
url(r '^hello/$', 'hello'),
url(r '^search/$', 'search'),
url(r '^contact/$', 'contact'),
url(r '^contact/thanks/$', ' thanks'),
)
urlpatterns += patterns( 'weblogs.views',
url(r '^hello/$', 'world'),
)
使用正規表達式的組操作來向views傳遞參數
正則表達式可以使用()來表示匹配成功的組,同時可以使用后向引用\1,\2...\n來表示不同的組,
在表達式中代表重復的匹配字串。

不用groups的話,返回的一個match對象。
在python中還可以為組命名,使用(?P<year>\d{4}),意思是這個組的名字為year,匹配的是4個數字
正因為有命名組和不命名組的這兩種使用,正好可以以兩種方式給view函數傳遞參數:
1. 不命名組以順序的方式傳值
# views.py
def count( self, a, b) :
....
#urls.py
(r '^num/(\d{4})/(\d{2})/$', count),
#結果
#對於請求/num/1234/78,產生的調用為count(1234, 78)
def count( self, a, b) :
....
#urls.py
(r '^num/(\d{4})/(\d{2})/$', count),
#結果
#對於請求/num/1234/78,產生的調用為count(1234, 78)
2. 命名組以字典的方式傳值
#urls.py
(r '^num/(?P<b>\d{4})/(?P<a>\d{2})/$', count),
#結果
#對於請求/num/1234/78,產生的調用為count(b=1234, a=78)
(r '^num/(?P<b>\d{4})/(?P<a>\d{2})/$', count),
#結果
#對於請求/num/1234/78,產生的調用為count(b=1234, a=78)
通過比較,使用命名組有更大的優勢,可以讓代碼一目了然,也不用擔心參數的順序搞錯。
還是最好不用混用命名組和非命名組,雖然Django不會報錯,但不太好,你懂的。
下面了解一下url匹配的過程:
1. 如果含有命名組,優先采用字典參數
2. 其余的非命名組將會以順序的方式傳遞到剩余的參數中
3. url中其它的選項,將會以字典的方式傳遞
#urls.py
urlpatterns = patterns( '',
#第三個選項值可用來傳遞一些附加信息,同時可以讓請求路徑簡潔一點
#否則有時候請求鏈接帶太多參數也不太好
(r '^foo/(?P<a>\d{2})$', count, { 'b' : 43}),
)
#結果
#對於請求/num/12,產生的調用為count(12, b=43)
urlpatterns = patterns( '',
#第三個選項值可用來傳遞一些附加信息,同時可以讓請求路徑簡潔一點
#否則有時候請求鏈接帶太多參數也不太好
(r '^foo/(?P<a>\d{2})$', count, { 'b' : 43}),
)
#結果
#對於請求/num/12,產生的調用為count(12, b=43)
當然,view的函數可以使用默認值,這樣,如果請求路徑中沒有匹配到值的話,會調用默認的值。
注意,一般來說默認值應該設置為字符串,這樣是和請求路徑匹配過來的值的類型保持一致。因為
它們都是字符串。
即使你匹配的是數學,傳入的還是數字的字符串,需要使用int()函數進行轉化成
真正的整數。
在這里復習一下,django到底會匹配請求路徑的哪一部分?
1. 給定請求路徑www.example.com/myapp/foo
只會匹配myapp/foo這個部分
2. 給定請求路徑www.example.com/myapp/foo?name=david
只會匹配myapp/foo這個部分
3. 給定請求路徑www.example.com/myapp/foo#name=david
只會匹配myapp/foo這個部分
請求路徑中也不會包含請求GET還是POST,任何匹配成功的請求,都會運行同一個函數,
但這樣做不太好,最好還是對GET和POST采用不同的處理,這里就要用到request對象,
之前也介紹過,它包含了豐富的信息。
def foo(request)
:
if request.method == 'POST' :
#handle post ...
#need to redirect
return HttpResponseRedirect( '/someurl/')
elif request.method == 'GET' :
#handle get
#just return response
return render_to_response( 'page.html')
if request.method == 'POST' :
#handle post ...
#need to redirect
return HttpResponseRedirect( '/someurl/')
elif request.method == 'GET' :
#handle get
#just return response
return render_to_response( 'page.html')
通過路徑匹配動態構造view中的函數
上代碼:
#views.py
def say_hello( self, person_name) :
print 'Hello, %s' % person_name
def say_goodbye( self, person_name) :
print 'Goodbye, %s' % person_name
#urls.py
urlpatterns = patterns( '',
(r '^say_hello/(\w+)$', say_hello),
(r '^say_goodbye/(\w+)/$', say_goodbye),
)
上面兩個函數基本是做同一件事,傳入一個人的名字,然后問候一句。
def say_hello( self, person_name) :
print 'Hello, %s' % person_name
def say_goodbye( self, person_name) :
print 'Goodbye, %s' % person_name
#urls.py
urlpatterns = patterns( '',
(r '^say_hello/(\w+)$', say_hello),
(r '^say_goodbye/(\w+)/$', say_goodbye),
)
所以稍微改動一下匹配規則,創建一個動態處理這兩件事的一個函數。
#views.py
def greet( self, person_name, greeting) :
print '%s, %s' % person_name
#urls.py
urlpatterns = patterns( '',
(r '^say_hello/(\w+)$', greet, { 'greeting' : 'Hello'}),
(r '^say_goodbye/(\w+)/$', greet, { 'greeing' : 'Goodbye'}),
)
def greet( self, person_name, greeting) :
print '%s, %s' % person_name
#urls.py
urlpatterns = patterns( '',
(r '^say_hello/(\w+)$', greet, { 'greeting' : 'Hello'}),
(r '^say_goodbye/(\w+)/$', greet, { 'greeing' : 'Goodbye'}),
)
可以看到url中的第三個選項值可以傳入額外的信息,保持請求路徑的簡潔。
同時,這個選項值還可以傳入model類,使用委托的方式,讓你的view函數功能更加動態。
還可以傳入模塊名稱,讓你不用把模塊名寫死在render_to_response函數中。
還有一點需要注意,當命名組的名字和第三個選項值的名字相同時,Django會使用第三個選項值,
因為它的優先級更高。
使用閉包和可變長度參數來重構views函數
這里要用的概念有:
1. 函數對象也可以當參數進行傳遞
2. 閉包,可以簡單的理解為是函數中定義函數
3. 參數中*args代表可變長度元組參數,也叫非關鍵字參數,**args代表可變長度字典類參數,也叫關鍵字參數

注意報錯內容,關鍵字參數一定要放在最右邊
重構例子:
def my_view1(request)
:
if not request.user.is_authenticated() :
return HttpResponseRedirect( '/accounts/login/')
# ...
return render_to_response( 'template1.html')
def my_view2(request) :
if not request.user.is_authenticated() :
return HttpResponseRedirect( '/accounts/login/')
# ...
return render_to_response( 'template2.html')
def my_view3(request) :
if not request.user.is_authenticated() :
return HttpResponseRedirect( '/accounts/login/')
# ...
return render_to_response( 'template3.html')
#這上面三個方法,一開始都要進行驗證,這有點重復
#下面添加一個新的方法
def requires_login(view) :
def new_view(request, *args, **kwargs) :
if not request.user.is_authenticated() :
return HttpResponseRedirect( '/accounts/login/')
return view(request, *args, **kwargs)
return new_view
#使用閉包,定義相同的驗證的部分,並返回相應的函數對象,這樣就可以
#在上面三個函數中實現自己不同的代碼了。可以把驗證的部分統統去掉。
#另外urls.py可以改成
from django.conf.urls.defaults import *
from mysite.views import requires_login, my_view1, my_view2, my_view3
urlpatterns = patterns( '',
(r '^view1/$', requires_login(my_view1)),
(r '^view2/$', requires_login(my_view2)),
(r '^view3/$', requires_login(my_view3)),
)
if not request.user.is_authenticated() :
return HttpResponseRedirect( '/accounts/login/')
# ...
return render_to_response( 'template1.html')
def my_view2(request) :
if not request.user.is_authenticated() :
return HttpResponseRedirect( '/accounts/login/')
# ...
return render_to_response( 'template2.html')
def my_view3(request) :
if not request.user.is_authenticated() :
return HttpResponseRedirect( '/accounts/login/')
# ...
return render_to_response( 'template3.html')
#這上面三個方法,一開始都要進行驗證,這有點重復
#下面添加一個新的方法
def requires_login(view) :
def new_view(request, *args, **kwargs) :
if not request.user.is_authenticated() :
return HttpResponseRedirect( '/accounts/login/')
return view(request, *args, **kwargs)
return new_view
#使用閉包,定義相同的驗證的部分,並返回相應的函數對象,這樣就可以
#在上面三個函數中實現自己不同的代碼了。可以把驗證的部分統統去掉。
#另外urls.py可以改成
from django.conf.urls.defaults import *
from mysite.views import requires_login, my_view1, my_view2, my_view3
urlpatterns = patterns( '',
(r '^view1/$', requires_login(my_view1)),
(r '^view2/$', requires_login(my_view2)),
(r '^view3/$', requires_login(my_view3)),
)
使用include()引用其它路徑配置文件
from django.conf.urls.defaults
import
*
urlpatterns = patterns( '',
(r '^weblog/', include( 'mysite.blog.urls')),
(r '^photos/', include( 'mysite.photos.urls')),
(r '^about/$', 'mysite.views.about'),
)
urlpatterns = patterns( '',
(r '^weblog/', include( 'mysite.blog.urls')),
(r '^photos/', include( 'mysite.photos.urls')),
(r '^about/$', 'mysite.views.about'),
)
一個project有一個總的urls.py,各個app也可以自己建立自己的urls.py,不過都需要使用include()
函數在project的urls.py文件進行注冊。這樣利用你的項目管理。
可以注意到使用了include的路徑的匹配表達式沒有加上'$',這是因為Django的機制是
把當前匹配的請求路徑的部分去除,剩下的部分傳到include所指定的路徑配置中進行匹配。
比如一個請求路徑/weblog/7000/過來,匹配部分是weblog/7000/,又因為weblog/匹配成功,
就把剩余的部分7000/傳入到mysit.blog.urls文件中進行下一步的匹配。
當使用include()的路徑匹配中含有正則組的時候,這個匹配的參數會傳入對include中所指定的urlconf中
所有的函數里,不管函數是接受這個參數,顯然很容易報錯,所以這種做法不太好。
同理,如果你在include的路徑匹配中使用了第三個選項參數,也會強制傳入指定的urlconf中的所有函數中。
也不太好。