Django網址分配器

一個簡潔、優雅的網址(URL)是建置一個高品質網頁應用程式的重要細節之一。Django讓你設計你想要的網址樣式,而且不會受到框架系統的限制(有些系統會定義一些和框架有關的樣式,例如網址包含view的詞彙)。

註:網址,其英文原是Uniform Resource Locator,縮寫為URL,若依照字面翻譯是"統一資源定位地址",這應該不是一般人看得懂的,因此在這裡都用"網址(URL)"。

在Django網頁框架之中,網址是由一個設定檔所配置,這個檔案是urls.py(稱之為URLconf模組),或者說urls.py是網址分配器(URL Dispatcher)。這個urls.py實際上是一個Python模組(module)檔案,內容包含Python程式碼,功能是讓你將網址樣式映射(例如正規表示式)至對應的回呼函式(例如視圖中的callback function)。

這樣的網址映射(mapping)關係可以依照暫時或長期的需求設定,也可以參考到其他映射關係,由於設定檔本身就是Python程式碼,所以可以建置成動態調整網址的樣式。

Django如何處理HTTP請求

當一個Django的網站接收到HTTP請求時,網頁框架系統依據演算法將決定哪個函式接收,詳細的HTTP請求處理順序如下:

    1. Django首先判斷系統的URLconf模組是否使用(也就是urls.py),一般來說,在設定檔中你會指定ROOT_URLCONF參數,此參數指向一個Python檔案。如果你在HttpRequest物件中設定urlconf屬性,則Django不會使用系統的URLconf模組。

    2. 第一步是尋找URLconf模組,接著Django載入檔案找尋urlpatterns變數。urlpatterns變數的資料類型必須是List,並且是django.conf.urls.defaults.patterns()所回傳的值。

    3. urlpatterns變數已經讀取到了,現在Django將"依序"比對每個HTTP請求是否和URL樣式相同,當遇到第一個相同的URL樣式時,將停止搜尋比對。

    4. 一旦URL樣式(正規表示式)相符,Django將載入所屬對應的View模組,將HttpRequest物件做為第一個引數(argument)呼叫此View的函式,剩下的URL參數也將一併傳入。

命名群組(Named Groups)

這是Django網址分配器更進階的使用方法,若將範例1使用命名的正規表示式群組( named regular-expression groups )寫法,則結果如下所示:

urlpatterns = patterns('',

(r'^articles/2003/$', 'news.views.special_case_2003'),

(r'^articles/(?P<year>\d{4})/$', 'news.views.year_archive'),

(r'^articles/(?P<year>\d{4})/(?P<month>\d{2})/$', 'news.views.month_archive'),

(r'^articles/(?P<year>\d{4})/(?P<month>\d{2})/(?P<day>\d+)/$', 'news.views.article_detail'),

)

可以看到,有地方改用了(?P<name>pattern)的樣式撰寫,此語法是Python語言的Regular expression operations,其中的name是指群組名稱,而pattern是正規表示式。採用這種命名群組的語法,Django網址分配器的結果將和範例1相同,但是,有一些細微的差異,特別注意!

採用命名群組的方式可以將(?P<name>pattern)的name當作變數傳遞給視圖(View),這樣說比較清楚:

    • 網址/articles/2005/03/對應至函式news.views.month_archive(request, year='2005', month='03')

    • 網址/articles/2003/03/3/對應至函式news.views.article_detail(request, year='2003', month='03', day='3')

實際上,利用命名群組設定Django網址分配器,優點是函式的參數不會亂掉而發生執行異常的問題(開發時常常修改code的情況),缺點是這樣的寫法較為冗長。

原始的urls.py檔案內容

from django.conf.urls.defaults import *

# Uncomment the next two lines to enable the admin:

# from django.contrib import admin

# admin.autodiscover()

urlpatterns = patterns('',

# Example:

# (r'^WebApp/', include('WebApp.foo.urls')),

# Uncomment the admin/doc line below and add 'django.contrib.admindocs'

# to INSTALLED_APPS to enable admin documentation:

# (r'^admin/doc/', include('django.contrib.admindocs.urls')),

# Uncomment the next line to enable the admin:

# (r'^admin/', include(admin.site.urls)),

)

範例1

from django.conf.urls.defaults import *

urlpatterns = patterns('',

(r'^articles/2003/$', 'news.views.special_case_2003'),

(r'^articles/(\d{4})/$', 'news.views.year_archive'),

(r'^articles/(\d{4})/(\d{2})/$', 'news.views.month_archive'),

(r'^articles/(\d{4})/(\d{2})/(\d+)/$', 'news.views.article_detail'),

)

範例1是一個常見的urls.py樣子,注意到幾項事情:

    • 第一列程式碼:from django.conf.urls.defaults import *是必要的,此套件中包含patterns()函式的宣告。

    • 如果希望URL中的字串要成為函式呼叫的變數,記得加上圓括號(parenthesis),如第二個至第四個的樣式。

    • 最前面不需要加入斜線(slash),因為每個網址已經自動包含。

    • 正規表示式的前端建議加上'r',所代表的是raw字串,Python將不會忽略字串中的任何表示式。

範例1的函式呼叫關係:

    • 網址/articles/2005/03/對應至

    • 函式news.views.month_archive(request, '2005', '03')

    • 網址/articles/2005/3/沒有任何函式對應,urlpatterns中需要兩個數字才相符

    • 網址/articles/2003/對應至

    • 函式news.views.special_case_2003(request),網址是依序比較,第一個先符合條件。

    • 網址/articles/2003沒有任何函式對應,因為urlpatterns都需要斜線結尾。

    • 網址/articles/2003/03/3/對應至

    • 函式news.views.article_detail(request, '2003', '03', '3')