Handling HTTP requests 之URL dispatcher

綜述

設(shè)計(jì)一個(gè)web應(yīng)用的URLs,你需要新建一個(gè)Python模塊來(lái)進(jìn)行URL設(shè)置(URLconf)。這個(gè)模塊中純Python代碼的并且是一個(gè)URL patterns(正則表達(dá)式)與Python函數(shù)(views)之間的映射匹配。

Django完成一個(gè)網(wǎng)絡(luò)請(qǐng)求的過(guò)程。

<li>通常在setting.py文件中使用ROOT_URLCONF設(shè)置app的根URL,訪問(wèn)的HttpRequest對(duì)象有一個(gè)屬性是urlconf,對(duì)應(yīng)的值就是ROOT_URLCONF設(shè)置的值.
<li>Django加載上文中新建的模塊,并且尋找相應(yīng)urlpatterns。urlpatterns是一個(gè)包含django.conf.urls.url()實(shí)例的列表。
<li>Django從上到下搜索每一個(gè)URL pattern,在第一個(gè)能夠匹配的url()實(shí)例出停止。
<li>一旦匹配成功Django就會(huì)倒入并且調(diào)用一個(gè)函數(shù)(或者是一個(gè)class-based view)返回一個(gè)視圖。獲取試圖的參數(shù)通過(guò)以下方式傳遞
<ol>
<li>通過(guò)HttpRequest實(shí)例傳遞。
<li>url()實(shí)例的正則表達(dá)式中傳遞參數(shù)
<li>django.conf.urls.url() 關(guān)鍵字傳遞參數(shù)
</ol>
<li>如果沒(méi)有匹配或者匹配過(guò)程中出現(xiàn)異常,Django將拋出異常

<pre>
from django.conf.urls import url
from . import views
urlpatterns = [
url(r'^articles/2003/$', views.special_case_2003),
url(r'^articles/([0-9]{4})/$', views.year_archive),
url(r'^articles/([0-9]{4})/([0-9]{2})/$', views.month_archive),
url(r'^articles/([0-9]{4})/([0-9]{2})/([0-9]+)/$', views.article_detail),
]
</pre>
<li>一個(gè)http:www.XXX/articles/2005/03/請(qǐng)求將會(huì)匹配第三個(gè)入口,Django將會(huì)調(diào)用views.month_archive(request, '2005', '03')函數(shù)
<li>/articles/2005/3/不會(huì)任何匹配,因?yàn)榈谌齻€(gè)入口中需要月份參數(shù)是兩位數(shù)。
<li>/articles/2003/將會(huì)匹配第一個(gè)模式而不是第二個(gè)。因?yàn)榈谝粋€(gè)匹配成功了,就不會(huì)繼續(xù)往下走
<li>/articles/2003不會(huì)有任何的匹配。上文中的每個(gè)pattern的結(jié)尾都必須要有一個(gè)"/"
<li>/articles/2003/03/03/ 匹配最后一個(gè)模式。并且調(diào)用views.article_detail(request, '2003', '03', '03')函數(shù)。

采用 name groups

采用正則表達(dá)式(?P<name>pattern)patter來(lái)匹配,捕獲的之作為關(guān)鍵之參數(shù)傳遞給視圖表達(dá)式。
<pre>
from django.conf.urls import url
from . import views
urlpatterns = [
url(r'^articles/2003/$', views.special_case_2003),
url(r'^articles/(?P<year>[0-9]{4})/$', views.year_archive),
url(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/$', views.month_archive),
url(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/(?P<day>[0-9]{2})/$', views.article_detail),
]
</pre>

<li>/articles/2005/03/,將會(huì)調(diào)用 views.month_archive(request, year='2005', month='03')
<li>/articles/2003/03/03/ --->views.article_detail(request, year='2003', month='03', day='03')
同時(shí)傳遞給視圖函數(shù)的總是strings

URLconf結(jié)果

URLconf把一個(gè)requested URL當(dāng)成一個(gè)普通的Python string搜索。與請(qǐng)求的方法無(wú)關(guān)(POST、GET)
https://www.example.com/myapp/ --->匹配 myapp/
https://www.example.com/myapp/?page=3 ----->匹配 myapp/ 參數(shù)可以通過(guò)視圖函數(shù)的request對(duì)象實(shí)例相關(guān)方法回去 request.GET['page']

指定默認(rèn)值
<pre>

URLconf

from django.conf.urls import url
from . import views
urlpatterns = [
url(r'^blog/$', views.page),
url(r'^blog/page(?P<num>[0-9]+)/$', views.page),
]

View (in blog/views.py)

def page(request, num="1"):
# Output the appropriate page of blog entries, according to num.
...

在上面的例子中,兩個(gè)URL模式指向相同的視圖-views.page-。但是第一個(gè)模式并沒(méi)有從URL中捕捉到任何東西。如果第一個(gè)模式匹配,那么page()函數(shù)將使用它的默認(rèn)參數(shù)num“1”。如果第二個(gè)模式匹配,page()將使用regex捕獲的任何num值
</pre>

Including other URLconfs

include() 函數(shù),可以把urlpatterns放到別的地方去設(shè)置
<pre>
from django.conf.urls import include, url
urlpatterns = [
url(r'^community/', include('django_website.aggregator.urls')),
url(r'^contact/', include('django_website.contact.urls')),
]

把所有以community開(kāi)頭的請(qǐng)求都交由django_website.aggregator.urls去處理
</pre>

也是一種拆分urlpatterns臃腫的辦法
<pre>
from django.conf.urls import include, url
from apps.main import views as main_views
from credit import views as credit_views
extra_patterns = [
url(r'^reports/$', credit_views.report),
url(r'^reports/(?P<id>[0-9]+)/$', credit_views.report),
url(r'^charge/$', credit_views.charge),
]
urlpatterns = [
url(r'^$', main_views.homepage),
url(r'^help/', include('apps.help.urls')),
url(r'^credit/', include(extra_patterns)),
]

例子中/credit/reports/ 將有credit_views.report()處理。 現(xiàn)在 url(r'^credit/', include(extra_patterns))匹配,再在extra_patterns 尋找進(jìn)一步的匹配
</pre>
include()參數(shù)的獲取
<pre>

In settings/urls/main.py

from django.conf.urls import include, url
urlpatterns = [
url(r'^(?P<username>\w+)/blog/', include('foo.urls.blog')),
]

In foo/urls/blog.py

from django.conf.urls import url
from . import views
urlpatterns = [
url(r'^$', views.blog.index),
url(r'^archive/$', views.blog.archive),
]

diyinqianchang/blog/archive/ --->views.blog.archive(request,username='diyinqianchang')

def archive(request,username=None)
</pre>

Reverse resolution of URLs

在編寫(xiě)web程序時(shí),有時(shí)候會(huì)需要在網(wǎng)頁(yè)上嵌入一個(gè)連接,用于跳轉(zhuǎn)。也可以用設(shè)計(jì)好的pattern來(lái)產(chǎn)生匹配格式的網(wǎng)址。
<li>在templates中使用此url的標(biāo)記
<li>在Python代碼中reverse()函數(shù) reverse(viewname, urlconf=None, args=None, kwargs=None, current_app=None)
<li> get_absolute_url()函數(shù)

name 標(biāo)記
<pre>
from django.conf.urls import url
from . import views
urlpatterns = [
url(r'^articles/([0-9]{4})/$', views.year_archive, name='news-year-archive'),
]
<a href="{% url 'news-year-archive' 2015%}"></a>
</pre>

reverse()
<pre>
from django.urls import reverse
from django.http import HttpResponseRedirect
def redirect_to_year(request):
year = 2006
return HttpResponseRedirect(reverse('news-year-archive', args=(year,)))
</pre>

URL命名空間

URL 命名空間允許你反查到唯一的,即使不同的應(yīng)用使用相同的URL 名稱。第三方應(yīng)用始終使用帶命名空間的URL 是一個(gè)很好的實(shí)踐。

當(dāng)解析一個(gè)帶命名空間的URL(例如'polls:index')時(shí),Django 將切分名稱為多個(gè)部分,然后按下面的步驟查找:
<li>首先,Django 查找匹配的 application namespace, 在下面的例子中為'polls'。這將得到該應(yīng)用實(shí)例的一個(gè)列表。
<li>如果當(dāng)前應(yīng)用屬性被定義,Django將查找并返回那個(gè)實(shí)例的URL解析器。當(dāng)前應(yīng)用屬性可在reverse() 函數(shù)中通過(guò)current_app來(lái)指定。
<li>如果沒(méi)有當(dāng)前應(yīng)用。Django 將查找一個(gè)默認(rèn)的應(yīng)用實(shí)例。默認(rèn)的應(yīng)用實(shí)例是[instance namespace和application namespace 一致的那個(gè)實(shí)例(在下面例子中,polls的一個(gè)叫做'polls' 的實(shí)例)
<li>如果沒(méi)有默認(rèn)的應(yīng)用實(shí)例,Django 將挑選該應(yīng)用最后部署的實(shí)例,不管實(shí)例的名稱是什么。
<li>如果提供的命名空間與第1步中的application namespace 不匹配,Django 將嘗試直接將此命名空間作為一個(gè) instance namespace查找。

<pre>urls.py
from django.conf.urls import include, url
urlpatterns = [
url(r'^author-polls/', include('polls.urls', namespace='author-polls')),
url(r'^publisher-polls/', include('polls.urls', namespace='publisher-polls')),
]
</pre>

<pre>polls/urls.py
from django.conf.urls import url
from . import views
app_name = 'polls'
urlpatterns = [
url(r'^$', views.IndexView.as_view(), name='index'),
url(r'^(?P<pk>\d+)/$', views.DetailView.as_view(), name='detail'),
]
</pre>

本例中author-polls和publisher-polls都有相同連寫(xiě)的主頁(yè)和詳細(xì)鏈接,被include() 在polls/urls.py

<li>如果其中一個(gè)實(shí)例是當(dāng)前實(shí)例 —— 如果我們正在渲染'author-polls' 實(shí)例的detail 頁(yè)面 —— 'polls:index' 將解析成'author-polls' 實(shí)例的主頁(yè)面;例如下面兩個(gè)都將解析成"/author-polls/"。

<pre>第一和第二
reverse('polls:index', current_app=self.request.resolver_match.namespace)

{% url 'polls:index' %}
</pre>

<li>如果沒(méi)有當(dāng)前實(shí)例——假如說(shuō)我們?cè)谡军c(diǎn)的其他地方渲染頁(yè)面——'polls:index'將解析到最后注冊(cè)到polls的實(shí)例。因?yàn)闆](méi)有默認(rèn)的實(shí)例(實(shí)例命名空間為polls),將用注冊(cè)的polls的最后一個(gè)實(shí)例。它將是'publisher-polls',因?yàn)樗莡rlpatterns中最后一個(gè)聲明的.

<li>'author-polls:index' 將永遠(yuǎn)解析到 'author-polls' 實(shí)例的主頁(yè)('publisher-polls' 類似) 沒(méi)有用什么命名空間,用name來(lái)標(biāo)記

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容