国产一区二区精品久久_蜜桃狠狠狠狠狠狠狠狠狠_午夜视频精品_激情都市一区二区

當前位置:首頁 > 網站舊欄目 > 學習園地 > 設計軟件教程 > 翻譯www.djangobook.com之第八章:高級視圖和URL配置

翻譯www.djangobook.com之第八章:高級視圖和URL配置
2010-01-13 23:37:42  作者:  來源:
URL配置技巧
使方法import流化
看看下面的URL配置,基于第3章的例子:
Java代碼 復制代碼
  1. from django.conf.urls.defaults import *   
  2. from mysite.views import current_datetime, hours_ahead, hours_behind, now_in_chicago, now_in_london   
  3.   
  4. urlpatterns = patterns('',   
  5.     (r'^now/$', current_datetime),   
  6.     (r'^now/plus(\d{1,2})hours/$', hours_ahead),   
  7.     (r'^now/minus(\d{1,2})hours/$', hours_behind),   
  8.     (r'^now/in_chicago/$', now_in_chicago),   
  9.     (r'^now/in_london/$', now_in_london),   
  10. )  

前面第3章解釋到,URL配置里每行都包含了它相關的視圖方法,直接作為一個方法對象傳遞
這意味著有必要在模塊最上面import視圖方法
但是隨著Django程序越來越復雜,它的URL配置也隨之增加,維護這些imports將十分麻煩
對于每個新的視圖方法,你都要記得import它,并且使用這個方法的話import語句會變得很長
可以通過import views模塊本身來避免這種復雜,下面的URL配置的例子和上面的是相等的:
Java代碼 復制代碼
  1. from django.conf.urls.defaults import *   
  2. from mysite import views   
  3.   
  4. urlpatterns = patterns('',   
  5.     (r'^now/$', views.current_datetime),   
  6.     (r'^now/plus(\d{1,2})hours/$', views.hours_ahead),   
  7.     (r'^now/minus(\d{1,2})hours/$', views.hours_behind),   
  8.     (r'^now/in_chicago/$', views.now_in_chicago),   
  9.     (r'^now/in_london/$', views.now_in_london),   
  10. )  

Django提供另一種方式來在URL配置中指定視圖方法:你可以傳遞一個包含模塊名字和方法名字的字符串
而不是方法對象本身,繼續上面的例子:
Java代碼 復制代碼
  1. from django.conf.urls.defaults import *   
  2.   
  3. urlpatterns = patterns('',   
  4.     (r'^now/$''mysite.views.current_datetime'),   
  5.     (r'^now/plus(\d{1,2})hours/$''mysite.views.hours_ahead'),   
  6.     (r'^now/minus(\d{1,2})hours/$''mysite.views.hours_behind'),   
  7.     (r'^now/in_chicago/$''mysite.views.now_in_chicago'),   
  8.     (r'^now/in_london/$''mysite.views.now_in_london'),   
  9. )  

使用這種技術,沒有必要再import視圖方法,Django根據字符串描述的視圖方法的名字和路徑自動
在第一次訪問時import合適的視圖方法
另一種捷徑是當使用字符創技術時可以把通用的視圖前綴提取出來,我們的例子中,每個視圖字符串
都以'mysite.views'開始,它們是冗余的,我們可以把它作為第一個參數傳遞給patterns():
Java代碼 復制代碼
  1. from django.conf.urls.defaults import *   
  2.   
  3. urlpatterns = patterns('mysite.views',   
  4.     (r'^now/$''current_datetime'),   
  5.     (r'^now/plus(\d{1,2})hours/$''hours_ahead'),   
  6.     (r'^now/minus(\d{1,2})hours/$''hours_behind'),   
  7.     (r'^now/in_chicago/$''now_in_chicago'),   
  8.     (r'^now/in_london/$''now_in_london'),   
  9. )  

注意你不需在前綴末尾加上".",也不需在視圖字符串前面加".",Django會自動加上去
這兩種方式哪種更好?這取決于你的個人編碼風格和需求
使用字符串方式的優點:
1,更緊湊,因為不需要import視圖方法
2,如果你的視圖方法分布在幾個不同的Python模塊,這種方式更可讀和更易管理
使用方法對象方式的優點:
1,可以輕松包裝視圖方法,參考本章后面的“包裝視圖方法”
2,更“Pythonic”,更貼近Python傳統,如傳遞方法對象
兩種方式都是合法的,你甚至可以在同一URL配置里混用它們,選擇權在你手中

多種視圖前綴
實踐中如果你使用字符串技術,你很可能混合視圖,因為視圖沒有通用的前綴
盡管如此,你可以利用視圖前綴捷徑來減少冗余,只需將多個patterns()加到一起
舊的:
Java代碼 復制代碼
  1. from django.conf.urls.defaults import *   
  2.   
  3. urlpatterns = patterns('',   
  4.     (r'^/?$''mysite.views.archive_index'),   
  5.     (r'^(\d{4})/([a-z]{3})/$''mysite.views.archive_month'),   
  6.     (r'^tag/(\w+)/$''weblog.views.tag'),   
  7. )  

新的:
Java代碼 復制代碼
  1. from django.conf.urls.defaults import *   
  2.   
  3. urlpatterns = patterns('mysite.views',   
  4.     (r'^/?$''archive_index'),   
  5.     (r'^(\d{4})/([a-z]{3})/$','archive_month'),   
  6. )   
  7.   
  8. urlpatterns += patterns('weblog.views',   
  9.     (r'^tag/(\w+)/$''tag'),   
  10. )  

Django只關心是否有一個模塊級的變量urlpatterns,而這個變量可以被動態構建,像上面的例子一樣

命名組
到目前為止在我們所有的URL配置的例子中,我們使用了簡單的,未命名的正則表達式組
即我們用括號包括我們想捕獲的部分URL,Django像傳遞位置參數一樣把這些捕獲的文本傳遞給視圖方法
在更高級的使用中,可以使用命名的正則表達式組來捕獲URL并且傳遞關鍵字參數給視圖
關鍵字參數與位置參數
一個Python方法可以使用關鍵字參數或者位置參數來調用,它們是一樣的
在關鍵字參數調用中,你指定你想傳遞的參數名和值
在位置參數調用中,你簡單的傳遞參數而不指定哪個參數匹配哪個值,關聯在參數順序中隱含
看看下面這個簡單的方法:
Java代碼 復制代碼
  1. def sell(item, price, quantity):   
  2.     print "Selling %s unit(s) of %s at %s" % (quantity, item, price)  

你可以按方法定義的參數順序傳遞參數來使用位置參數調用:sell('Socks', '$2.50', 6)
你也可以指定參數名和參數值來使用關鍵字參數調用,下面的語句是相等的:
Java代碼 復制代碼
  1. sell(item='Socks', price='$2.50', quantity=6)   
  2. sell(item='Socks', quantity=6, price='$2.50')   
  3. sell(price='$2.50', item='Socks', quantity=6)   
  4. sell(price='$2.50', quantity=6, item='Socks')   
  5. sell(quantity=6, item='Socks', price='$2.50')   
  6. sell(quantity=6, price='$2.50', item='Socks')  

在Python正則表達式中,命名組的語法是(?P<name>pattern),其中name是組的名字,pattern是要匹配的模式
下面是URL配置的使用未命名組的例子:
Java代碼 復制代碼
  1. from django.conf.urls.defaults import *   
  2. from mysite import views   
  3.   
  4. urlpatterns = patterns('',   
  5.     (r'^articles/(\d{4})/$', views.year_archive),   
  6.     (r'^articles/(\d{4})/(\d{2})/$', views.month_archive),   
  7. )  

這里我們使用同樣的URL配置,但是使用命名組來重寫:
Java代碼 復制代碼
  1. from django.conf.urls.defaults import *   
  2. from mysite import views   
  3.   
  4. urlpatterns = patterns('',   
  5.     (r'^articles/(?P<year>\d{4})/$', views.year_archive),   
  6.     (r'^articles/(?P<year>\d{4})/(?P<month>\d{2})/$', views.month_archive),   
  7. )  

下面的例子和上面的例子達到的是同樣的目的,但是有一個微小的差別,它捕獲的值傳遞給視圖方法時
使用的是關鍵字參數而不是位置參數
例如,使用未命名組,對/articles/2006/03的訪問將導致下面的方法調用:
month_archive(request, '2006', '03')
使用命名組,同樣的請求則會導致下面的方法調用:
month_archive(request, year='2006', month='03')
實踐中使用命名組會讓你的URL配置更清晰和帶來更少的參數順序bugs,而且你可以重排視圖方法中
定義的參數的順序
按照上面的例子,如果你想改變URL使month在year的前面,并且我們使用未命名組,我們必須記得去改
month_archive視圖的參數順序,而如果我們使用命名組,在URL中改變捕獲的參數的順序不會對視圖造成影響
當然,命名組的好處也帶來一些簡潔上的代價,一些開發人員認為命名組的語法丑陋而且冗長

匹配和組算法
如果你同時命名組和未命名組使用兩種方式來處理相同的URL模式,你應該清楚Django怎樣處理這種特殊情況
下面是URL配置解析器的算法:
1,如果有命名的參數,Django將使用它,并且忽略未命名的參數
2,否則,Django視所有的未命名參數為位置參數傳遞
3,兩種參數都有的情況下,Django將傳遞一些額外的關鍵字參數作為關鍵字參數
參考下面的“向視圖方法傳遞額外選項”

向視圖方法傳遞額外選項
有時候你發現你些的視圖方法很相似,只有一些很少的差別
例如,你有兩個視圖,它們的內容除了使用的模板不同其它都一樣:
Java代碼 復制代碼
  1. # urls.py   
  2.   
  3. from django.conf.urls.defaults import *   
  4. from mysite import views   
  5.   
  6. urlpatterns = patterns('',   
  7.     (r'^foo/$', views.foo_view),   
  8.     (r'^bar/$', views.bar_view),   
  9. )   
  10.   
  11. # views.py   
  12.   
  13. from django.shortcuts import render_to_response   
  14. from mysite.models import MyModel   
  15.   
  16. def foo_view(request):   
  17.     m_list = MyModel.objects.filter(is_new=True)   
  18.     return render_to_response('template1.html', {'m_list': m_list})   
  19.   
  20. def bar_view(request):   
  21.     m_list = MyModel.objects.filter(is_new=True)   
  22.     return render_to_response('template2.html', {'m_list': m_list})  

我們在重復我們自己,這是不優雅的
首先你可能想通過使用同樣的視圖處理兩種URL來減少冗余,用括號括住URL來捕獲它,并且在視圖里
通過URL檢查來決定模板:
Java代碼 復制代碼
  1. # urls.py   
  2.   
  3. from django.conf.urls.defaults import *   
  4. from mysite import views   
  5.   
  6. urlpatterns = patterns('',   
  7.     (r'^(foo)/$', views.foobar_view),   
  8.     (r'^(bar)/$', views.foobar_view),   
  9. )   
  10.   
  11. # views.py   
  12.   
  13. from django.shortcuts import render_to_response   
  14. from mysite.models import MyModel   
  15.   
  16. def foobar_view(request, url):   
  17.     m_list = MyModel.objects.filter(is_new=True)   
  18.     if url == 'foo':   
  19.         template_name = 'template1.html'  
  20.     elif url == 'bar':   
  21.         template_name = 'template2.html'  
  22.     return render_to_response(template_name, {'m_list': m_list})  

這種方案的問題是它吧URL和你的代碼耦合在了一起,如果你想把/foo/改名為/fooey/,你必須記得去
更改視圖代碼
優雅的方式涉及到一個交額外URL配置選項的特性,URL配置中每個模式可能包含了另外一項:一個關鍵字
參數的字典,它將被傳遞到視圖方法中
我們可以像下面這樣重寫我們的例子:
Java代碼 復制代碼
  1. # urls.py   
  2.   
  3. from django.conf.urls.defaults import *   
  4. from mysite import views   
  5.   
  6. urlpatterns = patterns('',   
  7.     (r'^foo/$', views.foobar_view, {'template_name''template1.html'}),   
  8.     (r'^bar/$', views.foobar_view, {'template_name''template2.html'}),   
  9. )   
  10.   
  11. # views.py   
  12.   
  13. from django.shortcuts import render_to_response   
  14. from mysite.models import MyModel   
  15.   
  16. def foobar_view(request, template_name):   
  17.     m_list = MyModel.objects.filter(is_new=True)   
  18.     return render_to_response(template_name, {'m_list': m_list})  

你可以看到,例子中URL配置指定了template_name,視圖方法只是把它當作另一個參數
額外URL配置選項技術是向視圖方法傳遞額外的信息的很好的方式,它在Django綁定的一些
程序中用到,尤其是我們將在第9章碰到的generic views系統
下面是關于怎樣使用額外URL配置選項技術的一些方法

偽造捕獲的URL配置值
假設你已經有一些匹配模式的視圖,但是還有一個URL使用同樣的視圖邏輯卻和模式不匹配
這種情況下你可以通過額外URL配置選項偽造捕獲的URL值來處理具有相同視圖的額外的URL
例如,你可能有一個從特殊日期顯示數據的程序,像下面的URL:
Java代碼 復制代碼
  1. /mydata/jan/01/   
  2. /mydata/jan/02/   
  3. /mydata/jan/03/   
  4. # ...   
  5. /mydata/dec/30/   
  6. /mydata/dec/31/  

這很簡單就可以處理,你可以像下面這樣捕獲URL(使用命名組語法):
Java代碼 復制代碼
  1. urlpatterns = patterns('',   
  2.     (r'^mydata/(?P<month>\w{3})/(?P<day>\d\d)/$', views.my_view),   
  3. )  

視圖方法可能是這樣:
Java代碼 復制代碼
  1. def my_view(request, month, day):   
  2.     # ....  

這非常直接,沒有我們沒遇到過的,當你像增加另一個使用my_view的URL并且這個URL不包括month
或day的時候,技巧就出現了
例如你想增加另一個URL /mydata/birthday/,而它應該等同與/mydata/jan/06,我們可以像下面這樣
利用額外URL配置選項:
Java代碼 復制代碼
  1. urlpatterns = patterns('',   
  2.     (r'^mydata/birthday/$', views.my_view, {'month''jan''day''06'}),   
  3.     (r'^mydata/(?P<month>\w{3})/(?P<day>\d\d)/$', views.my_view),   
  4. )  

這里很酷的地方是,我們根本不需要改變我們的視圖方法,視圖方法僅僅關心它可以得到month和day參數
它不關心這些參數是否來自于URL捕獲本身或者額外參數

讓視圖一般化
在代碼中提取公共部分是很好的編程實踐,例如我們有下面兩個Python方法:
Java代碼 復制代碼
  1. def say_hello(person_name):   
  2.     print 'Hello, %s' % person_name   
  3.   
  4. def say_goodbye(person_name):   
  5.     print 'Goodbye, %s' % person_name  

我們可以把問候語提取出來讓它成為一個參數:
Java代碼 復制代碼
  1. def greet(person_name, greeting):   
  2.     print '%s, %s' % (greeting, person_name)  

你可以通過使用額外URL配置參數把這個哲學應用到你的Django視圖中去
這樣你就可以創建高級抽象視圖,例如:
Java代碼 復制代碼
  1. # urls.py   
  2.   
  3. from django.conf.urls.defaults import *   
  4. from mysite import views   
  5.   
  6. urlpatterns = patterns('',   
  7.     (r'^events/$', views.event_list),   
  8.     (r'^blog/entries/$', views.entry_list),   
  9. )   
  10.   
  11. # views.py   
  12.   
  13. from django.shortcuts import render_to_response   
  14. from mysite.models import Event, BlogEntry   
  15.   
  16. def event_list(request):   
  17.     obj_list = Event.objects.all()   
  18.     return render_to_response('mysite/event_list.html', {'event_list': obj_list})   
  19.   
  20. def entry_list(request):   
  21.     obj_list = BlogEntry.objects.all()   
  22.     return render_to_response('mysite/blogentry_list.html', {'entry_list': obj_list})  

兩個視圖做的是同一件事情,它們都負責顯示對象列表,因此讓我們把要顯示的對象的類型抽象出來:
Java代碼 復制代碼
  1. # urls.py   
  2.   
  3. from django.conf.urls.defaults import *   
  4. from mysite import models, views   
  5.   
  6. urlpatterns = patterns('',   
  7.     (r'^events/$', views.object_list, {'model': models.Event}),   
  8.     (r'^blog/entries/$', views.object_list, {'model': models.BlogEntry}),   
  9. )   
  10.   
  11. # views.py   
  12.   
  13. from django.shortcuts import render_to_response   
  14.   
  15. def object_list(request, model):   
  16.     obj_list = model.objects.all()   
  17.     template_name = 'mysite/%s_list.html' % model.__name__.lower()   
  18.     return render_to_response(template_name, {'object_list': obj_list})  

通過這些小改動,我們突然就有了一個可重用的,模型不可知的視圖!
從現在開始,任何時候我們需要一個對象列表的視圖,我們都可以簡單的重用object_list視圖
而不是寫視圖代碼,下面是關于我們做的事情的注意:
1,我們直接傳遞模型類作為model參數,額外URL配置選項字典可以傳遞任何類型的Python對象
2,model.objects.all()這一行是一個鴨子類型:“如果它走起來像鴨子,說話像鴨子,我們就認為
它是一只鴨子”,注意代碼并不知道model是什么類型,唯一的前提是model有一個objects屬性
并且objects有一個all()方法
3,我們使用model.__name__.lower()來決定模板名,每個Python類都有__name__屬性,它返回類名
這個特性對于現在的情形特別有用,我們直到運行時才知道類的類型
4,這個例子和上一個例子的一點不同是,我們傳遞通用的變量名object_list到模板中
我們可以很容易改變這個變量名為blogentry_list或者event_list,我們把這個工作留給讀者作為練習
因為數據庫驅動的Web站點有許多通用的模式,Django帶來了使用額外技術的“generic views”來為你
節省時間,我們將在下一章講到Django內建的generic views

給予視圖配置選項
如果你發布一個Django程序,你的用戶可能想擁有一定程度上的配置
這種情況下,向你的視圖添加鉤子來應對人們可能需要一些配置選項是個好注意
你可以使用額外URL配置參數來達到這個目的
程序中一個常見的配置是模板名:
Java代碼 復制代碼
  1. def my_view(request, template_name):   
  2.     var = do_something()   
  3.     return render_to_response(template_name, {'var': var})  


捕獲值的優先級與額外選項
當有沖突時,額外URL配置參數要比捕獲的參數優先級高
換句話說,如果你的URL配置捕獲了一個命名組變量和一個額外URL配置參數,而它們的變量名相同
則額外URL配置參數值將被使用,例如下面的URL配置:
Java代碼 復制代碼
  1. from django.conf.urls.defaults import *   
  2.   
  3. urlpatterns = patterns('',   
  4.     (r'^mydata/(?P<id>\d+)/$', views.my_view, {'id'3}),   
  5. )  

在這里正則表達式和額外的字典都包含id參數,此時硬編碼的id具有更高的優先級
這意味著/mydata/2/或者/mydata/432432/將被當成id設為3看待,而不管URL所捕獲的值
敏銳的讀者可能注意到這種情況下,在正則表達式里面捕獲id是純粹在浪費時間
因為它的值一直會被字典的值覆蓋
這些敏銳的讀者是正確的,我們講這些內容只是想幫助你避免錯誤

使用默認視圖參數
另外一個方便的技巧是指定視圖的默認參數,它告訴視圖如果一個參數值是none則使用默認值,例如:
Java代碼 復制代碼
  1. # urls.py   
  2.   
  3. from django.conf.urls.defaults import *   
  4.   
  5. urlpatterns = patterns('',   
  6.     (r'^blog/$', views.page),   
  7.     (r'^blog/page(?P<num>\d+)/$', views.page),   
  8. )   
  9.   
  10. # views.py   
  11.   
  12. def page(request, num="1"):   
  13.     # Output the appropriate page of blog entries, according to num.   
  14.     # ...  

這里兩個URL模式指向了同一個視圖views.page,但是第一個模式不會從URL捕獲任何東西
如果第一個模式匹配了,page()方法講使用num的默認參數“1”,如果第二個模式匹配了
page()講使用正則表達式捕獲的num值
和配置選項一起使用這個技術很常見,下面的例子對給予視圖配置選項的例子做了小小改進:
Java代碼 復制代碼
  1. def my_view(request, template_name='mysite/my_view.html'):   
  2.     var = do_something()   
  3.     return render_to_response(template_name, {'var': var})  


特殊情況下的視圖
有時候你在URL配置里有一個處理很多URL的模式但是你需要特別指出其中一個
這種情況下,使用URL配置中把特殊情況放在首位的線性處理方式
例如,Django的admin站點中“添加對象”頁面是如下配置的:
Java代碼 復制代碼
  1. urlpatterns = patterns('',   
  2.     # ...   
  3.     ('^([^/]+)/([^/]+)/add/$''django.contrib.admin.views.main.add_stage'),   
  4.     # ...   
  5. )  

這將匹配像/myblog/entries/add/和/auth/groups/add/這樣的URL
盡管如此,對于用戶對象的添加頁面/auth/user/add/是個特殊情況,例如它不會顯示所有的表單域,
它顯示兩個密碼域等等,我們可以通過在視圖中特別指出來以解決這個問題:
Java代碼 復制代碼
  1. def add_stage(request, app_label, model_name):   
  2.     if app_label == 'auth' and model_name == 'user':   
  3.         # do special-case code   
  4.     else:   
  5.         # do normal code  

但是它并不優雅,因為它把URL邏輯放在視圖中,更優雅的方式是我們利用URL配置是從頂向下解析的方案:
Java代碼 復制代碼
  1. urlpatterns = patterns('',   
  2.     # ...   
  3.     ('^auth/user/add/$''django.contrib.admin.views.auth.user_add_stage'),   
  4.     ('^([^/]+)/([^/]+)/add/$''django.contrib.admin.views.main.add_stage'),   
  5.     # ...   
  6. )  

這樣的話對于/auth/user/add/的請求將會被user_add_stage視圖處理,盡管URL也匹配第二種模式
它會先匹配上面的模式(這是短路邏輯)

從URL捕獲文本的注意點
每個被捕獲的參數像普通的Python字符串一樣被傳遞給視圖,而不管正則表達式匹配的類型
例如,下面的URL配置: