我們在中級篇中學會了如何進行反向解析,但是有這樣一個問題,在為 url 命名的時候,名字不能重復,否則會導致各種各樣的問題。在 url 還少的時候保證不重名還是比較簡單的,但是 url 多起來以后就比較難了。為了解決這樣的問題,可以在 url 中加一個前綴。例如,我有一個 url 的名字叫做 'comment' ,此時,我可以為其加一個前綴,這個前綴通常是 app 名,例如:'myapp-comment'。
這也是django所推薦的命名方式,但是這樣始終是治標不治本。此時,我們就要學習 django 中 url 命名空間了。
URL 命名空間
簡介:
URL 命名空間允許你反查到唯一的命名 URL,即使在不同的應用中使用相同的 URL 名稱。(也就是可以在不同的app中使用相同的名稱,為有命名困難症的程序員帶來了福音)
根據經驗,第三方應用應該始終使用帶命名空間的URL 。類似地,它還允許你在一個應用有多個實例部署的情況下反查URL。換句話講,因為一個應用的多個實例共享相同的命名URL,命名空間將提供一種區分這些命名 URL 的方法。
在一個站點上,正確使用 URL 命名空間的 Django 應用可以部署多次。例如,django.contrib.admin 具有一個 AdminSite 類,它允許你很容易地部署多個管理站點的實例。在下面的例子中,我們將討論在兩個不同的地方部署教程中的polls 應用,這樣我們可以為兩種不同的用戶(作者和發布者)提供相同的功能。
一個URL 命名空間有兩個部分,它們都是字符串:
- 應用命名空間:
- 它表示正在部署的應用的名稱。一個應用的每個實例具有相同的應用命名空間。例如,可以預見Django 的管理站點的應用命名空間是'admin'。
- 實例命名空間:
- 它表示應用的一個特定的實例。實例的命名空間在你的全部項目中應該是唯一的。但是,一個實例的命名空間可以和應用的命名空間相同。它用於表示一個應用的默認實例。例如,Django 管理站點實例具有一個默認的實例命名空間'admin'。
URL 的命名空間使用':' 操作符指定。例如,管理站點應用的主頁使用'admin:index'。它表示'admin' 的一個命名空間和'index' 的一個命名URL。
命名空間也可以嵌套。命名URL'sports:polls:index' 將在命名空間'polls'中查找'index',而poll 定義在頂層的命名空間'sports' 中。
反查帶命名空間的URL
我們在中級篇中了解到了 url 反查帶來的變量,而在中級篇中,都是使用 name 進行反查,這里來看看如何對帶命名空間的 url 進行反查。
例子:
from django.conf.urls import include, url urlpatterns = [ url(r'^author-polls/', include('polls.urls', namespace='author-polls', app_name='polls')), url(r'^publisher-polls/', include('polls.urls', namespace='publisher-polls', app_name='polls')), ]
from django.conf.urls import url from . import views urlpatterns = [ url(r'^$', views.IndexView.as_view(), name='index'), url(r'^(?P<pk>\d+)/$', views.DetailView.as_view(), name='detail'), ... ]
反查的方法和中級篇的一樣,在模板中:
{% url 'polls:index' %}
在基於類的視圖的方法中:
reverse('polls:index', current_app=self.request.resolver_match.namespace)
另外,注意,在模板中的反查需要添加 request 的 current_app 屬性,像這樣:
def render_to_response(self, context, **response_kwargs): self.request.current_app = self.request.resolver_match.namespace return super(DetailView, self).render_to_response(context, **response_kwargs)
這時,會有同學有疑問了, polls 這個應用命名空間設置了兩行呀,那 polls 下的 index 到底指的是哪個?
這個時候,就要看 django 的查找順序了:
1.如果當前有實例,也就是說我們通過 url 訪問到了某個處理函數,這個函數進行反向查詢的時候,例如我訪問的是:author-polls/ ,這個 url 對應的處理函數要進行反向解析,此時它要解析 'polls:detail'。那么將解析到 author-polls/(?P<pk>\d+)/$ 中,也就是有實例的優先在該實例空間中查詢。
2.如果沒有實例,但是有默認的實例空間,例如 app_name='polls',namespace='polls' ,和應用空間同名,這樣的就叫做默認實例空間。在沒有訪問實例的時候,就匹配到默認實例空間中。
3.如果沒有實例,也沒有默認實例空間,那么誰是最后注冊的就選誰,例子中的 namespace='publisher-polls' 就是最后一個注冊的(也就是下面的)。
注意:
因為實例空間要是唯一的,所以使用 namespace:name 的模式應該也是唯一匹配的,例如這里的'author-polls:index' 將永遠解析到 'author-polls' 實例的主頁('publisher-polls' 類似)。
URL 命名空間和被包含的URLconf
被包含的URLconf 的命名空間可以通過兩種方式指定。
首先,在你構造你的URL 模式時,你可以提供 應用 和 實例的命名空間給 include() 作為參數。例如
url(r'^polls/', include('polls.urls', namespace='author-polls', app_name='polls')),
這將包含 polls.urls 中定義的URL 到應用命名空間 'polls'中,其實例命名空間為'author-polls'。
其次,你可以include 一個包含嵌套命名空間數據的對象。如果你include() 一個url() 實例的列表,那么該對象中包含的URL 將添加到全局命名空間。然而,你還可以include() 一個3個元素的元組:
(<list of url() instances>, <application namespace>, <instance namespace>)
注意這里的應用命名空間和實例命名空間是相反的。
實例:
from django.conf.urls import include, url from . import views polls_patterns = [ url(r'^$', views.IndexView.as_view(), name='index'), url(r'^(?P<pk>\d+)/$', views.DetailView.as_view(), name='detail'), ] url(r'^polls/', include((polls_patterns, 'polls', 'author-polls'))),
這樣會包含命名的URL模式進入到給定的應用和實例命名空間中。
與 url 相關的函數都在 django.conf.urls 中,下面看看里面都有哪些函數:
1. patterns(prefix, pattern_description, ...)
這是一個廢棄了的方法,在 django1.8 之前,urlpatterns 變量是該函數的實例。
例如:
from django.conf.urls import patterns, url urlpatterns = patterns('', url(r'^articles/([0-9]{4})/$', 'news.views.year_archive'), url(r'^articles/([0-9]{4})/([0-9]{2})/$', 'news.views.month_archive'), url(r'^articles/([0-9]{4})/([0-9]{2})/([0-9]+)/$', 'news.views.article_detail'), )
第一個參數是一個前綴,這里的所有表示處理函數的字符串都是以 'new.views' 開頭的,所有可以改寫成下面這種形式:
from django.conf.urls import patterns, url urlpatterns = patterns('news.views', url(r'^articles/([0-9]{4})/$', 'year_archive'), url(r'^articles/([0-9]{4})/([0-9]{2})/$', 'month_archive'), url(r'^articles/([0-9]{4})/([0-9]{2})/([0-9]+)/$', 'article_detail'), )
因為 patterns 是python的一個方法,而 python 中,一個方法最多接受 255 個參數,也就是說最多可以在一個 patterns 中寫 255-1 個 url,雖然這一般不會有什么影響,因為我們可以通過 include 方法來分離 url,而且這個方法返回的是一個列表,所以可以通過列表拼接的方式,來擴展,例如:
urlpatterns = patterns('', ... ) urlpatterns += patterns('', ... )
所以也只是現在最多一次性創建多少個 url 而已。
但是這個方法還是在 1.8 中廢棄了(當然你還可強行用,到 1.9.4 為止代碼並沒有被移除,以后的版本另算),以后直接使用 python 列表,列表中的元素是 url() 函數的實例就行了。而 python 的列表不限制長度,只看電腦的內存有沒有足夠的空間,所以也算是改進了。
2. static.static(prefix, view=django.views.static.serve, **kwargs)
如果你通過django的后台上傳了一張圖片,而你又想在前端顯示它。一般而言 django 只提供了靜態文件的支持(默認是 /static/ 開頭的url請求都視為靜態文件請求),而圖片上傳是通過 MEDIA_URL 來作為
請求的開頭的,但是如果不做特殊設置,是無法顯示的,此時,可以通過以下的方式:
from django.conf import settings from django.conf.urls.static import static urlpatterns = [ # ... the rest of your URLconf goes here ... ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
這是老版本的寫法,新版本中直接把函數放進列表中,作為列表中的一個元素就行。
注意:這里我們並沒有為 view 傳參,直接用默認的就行。
當然,這也只是在開發環境中使用,如果到了正式生產環境,這些東西還是老老實實用 web server ,例如Apache或者nginx之類的作為前方代理,django自帶的 web 服務估計並發量在20左右就會掛掉,而且還不是守護進程,也就是說掛掉了不會重啟,要多蛋疼有多蛋疼。
3. include(arg, namespace=None, app_name=None)
arg:可以接受一個字符串,表示被包含的模塊在哪里;也可以接受一個列表,這個列表是被包含的 url() 的實例;還可以接受一個元祖,元祖的第一個元素是一個被包含的列表,第二個元素是該列表的應用空間名,第三個元素是實例空間名。
namespace : 實例命名空間
app_name : 應用命名空間
4. url(regex, view, kwargs=None, name=None, prefix='')
regex:要匹配的 url。
view:該 url 的處理函數,可以是一個表示函數位置的字符串, 也可以是一個函數的實例。
kwargs: 一個字典,表示傳遞多余的參數。
name : 為 url 進行命名。
prefix : if prefix: view = prefix + '.' + view 表示在 view 前加上前綴。
5.各種 handler:
django 自帶錯誤處理視圖:
handler400 = 'django.views.defaults.bad_request'
handler403 = 'django.views.defaults.permission_denied'
handler404 = 'django.views.defaults.page_not_found'
handler500 = 'django.views.defaults.server_error'
在觸發相應的錯誤的時候,都會轉向默認的處理函數,當然我們也可以重寫它們,只有在 urls.py 中導入它們,並改寫成我們自己的處理函數的字符串表示就可以了。