當一個用戶請求Django 站點的一個頁面,下面是Django 系統決定執行哪一個Python url路徑對應代碼遵循的算法:html
ROOT_URLCONF
設置的值,可是若是傳入的HttpRequest
對象具備urlconf
屬性(由中間件設置),則其值將被用於代替ROOT_URLCONF
設置。urlpatterns
。 它是django.conf.urls.url()
實例的一個Python 列表。HttpRequest
實例。django.conf.urls.url()
的可選參數kwargs
覆蓋。(關鍵字參數命名組)對於高質量的Web 應用來講,使用簡潔、優雅的URL 模式是一個很是值得重視的細節。 Django 讓你爲所欲爲設計你的URL,不受框架束縛。python
下面是一個簡單的 URLconf:web
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), ]
注:正則表達式
^articles
而不是 ^/articles
。'r'
是可選的可是建議加上。 它告訴Python 這個字符串是「原始的」 —— 字符串中任何字符都不該該轉義。是否開啓URL訪問地址後面不爲/跳轉至帶有/的路徑的配置項 -----------------------APPEND_SLASH=True算法
from django.urls import path urlpatterns = [ path('articles/2003/', views.special_case_2003), path('articles/<int:year>/', views.year_archive), path('articles/<int:year>/<int:month>/', views.month_archive), path('articles/<int:year>/<int:month>/<slug:slug>/', views.article_detail), ]
一些請求的例子:django
/articles/2005/03/
請求將匹配列表中的第三個模式。 Django 將調用函數views.month_archive(request, '2005', '03')
。/articles/2005/3/
不匹配任何URL 模式,由於列表中的第三個模式要求月份應該是兩個數字。/articles/2003/
將匹配列表中的第一個模式不是第二個,由於模式按順序匹配,第一個會首先測試是否匹配。 請像這樣自由插入一些特殊的狀況來探測匹配的次序。 這裏,Django會調用函數views.special_case_2003(request)
/articles/2003
不匹配任何一個模式,由於每一個模式要求URL 以一個斜線結尾。/articles/2003/03/03/
將匹配最後一個模式。 Django 將調用函數views.article_detail(request, '2003', '03', '03')
。上面的示例使用簡單的、沒有命名的正則表達式組(經過圓括號)來捕獲URL 中的值並以位置 參數傳遞給視圖。 在更高級的用法中,可使用命名的正則表達式組來捕獲URL 中的值並以關鍵字 參數傳遞給視圖。app
在Python 正則表達式中,命名正則表達式組的語法是(?P<name>pattern)
,其中name
是組的名稱,pattern
是要匹配的模式。框架
下面是以上URLconf 使用命名組的重寫:函數
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), ]
這個實現與前面的示例徹底相同,只有一個細微的差異:捕獲的值做爲關鍵字參數而不是位置參數傳遞給視圖函數。 像這樣:工具
/articles/2005/03/
請求將調用views.month_archive(request, year='2005', month='03')
函數,而不是views.month_archive(request, '2005', '03')
。/articles/2003/03/03/
請求將調用函數views.article_detail(request, year='2003', month='03', day='03')
。在實際應用中,這意味你的URLconf 會更加明晰且不容易產生參數順序問題的錯誤 —— 你能夠在你的視圖函數定義中從新安排參數的順序。 固然,這些好處是以簡潔爲代價的;一些開發人員發現命名組語法醜陋並且太冗長。
下面是URLconf 解析器使用的算法,針對正則表達式中的命名組和非命名組:
根據傳遞額外的選項給視圖函數(下文),這兩種狀況下,多餘的關鍵字參數也將傳遞給視圖。
URLconf 在請求的URL 上查找,將它當作一個普通的Python 字符串。不包括GET和POST參數以及域名。
例如,http://www.example.com/myapp/ 請求中,URLconf 將查找myapp/。
在http://www.example.com/myapp/?page=3 請求中,URLconf 仍將查找myapp/
。
URLconf 不檢查請求的方法。換句話講,全部的請求方法 —— 同一個URL的POST
、GET
、HEAD
等等 —— 都將路由到相同的函數。
每一個捕獲的參數都做爲一個普通的Python 字符串傳遞給視圖,不管正則表達式使用的是什麼匹配方式。 例如,下面這行URLconf 中:
url(r'^articles/(?P<year>[0-9]{4})/$', views.year_archive),
views.year_archive()
的
year
參數將是一個字符串,不是整數,即便
[0-9]{4}
只匹配整數字符串。
有一個方便的小技巧是指定視圖參數的默認值。 下面是一個URLconf 和視圖的示例:
# 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. ...
在上面的例子中,兩個URL模式指向同一個視圖views.page
—— 可是第一個模式不會從URL 中捕獲任何值。 若是第一個模式匹配,page()
函數中num
將使用默認的參數值"1"
。 若是第二個模式匹配,page()
將使用正則表達式捕獲的num
值。
當Django 找不到一個匹配請求的URL 的正則表達式時,或者當拋出一個異常時,Django 將調用一個錯誤處理視圖。
這些狀況發生時使用的視圖經過4個變量指定。 它們的默認值足以知足大多數項目,但能夠經過覆蓋其默認值進一步進行自定義。
完整的細節請參見自定義錯誤視圖。
這些值能夠在你的根URLconf 中設置。 在其它URLconf 中設置這些變量將不會產生效果。
它們的值必須是可調用的或者是表示視圖的Python 完整導入路徑的字符串,能夠方便地調用它們來處理錯誤狀況。
這些值是:
handler400
—— 參見django.conf.urls.handler400
。handler403
—— 參見django.conf.urls.handler403
。handler404
—— 參見django.conf.urls.handler404
。handler500
—— 參見django.conf.urls.handler500
。在任什麼時候候,你的urlpatterns
均可以包含其它URLconf 模塊。 這實際上將一部分URL 放置於其它URL 下面。
例如,這裏是Django網站自己的URLconf摘錄。 它包含許多其它URLconf:
from django.conf.urls import include, url urlpatterns = [ # ... snip ... url(r'^community/', include('django_website.aggregator.urls')), url(r'^contact/', include('django_website.contact.urls')), # ... snip ... ]
注意,這個例子中的正則表達式沒有包含$
(字符串結束匹配符),可是包含一個末尾的斜槓。 每當Django 遇到include()
(django.conf.urls.include()
)時,它會去掉URL 中匹配的部分並將剩下的字符串發送給包含的URLconf 作進一步處理。
另一種包含其它URL 模式的方式是使用一個url()
實例的列表。 例如,請看下面的URLconf:
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/
URL將由credit_views.report()
Django視圖處理。
這能夠用於移除URL配置中重複的部分 例如,請看下面的URLconf:
from django.conf.urls import url from . import views urlpatterns = [ url(r'^(?P<page_slug>[\w-]+)-(?P<page_id>\w+)/history/$', views.history), url(r'^(?P<page_slug>[\w-]+)-(?P<page_id>\w+)/edit/$', views.edit), url(r'^(?P<page_slug>[\w-]+)-(?P<page_id>\w+)/discuss/$', views.discuss), url(r'^(?P<page_slug>[\w-]+)-(?P<page_id>\w+)/permissions/$', views.permissions), ]
咱們能夠改進它,經過只聲明共同的路徑前綴一次並將後面的部分分組:
from django.conf.urls import include, url from . import views urlpatterns = [ url(r'^(?P<page_slug>[\w-]+)-(?P<page_id>\w+)/', include([ url(r'^history/$', views.history), url(r'^edit/$', views.edit), url(r'^discuss/$', views.discuss), url(r'^permissions/$', views.permissions), ])), ]
def test01(request): return HttpResponse("teset01") def test02(request): return HttpResponse("teset02") def test03(request): return HttpResponse("teset03") urlpatterns = [ url("^test/", ([ url("^test01/", test01), url("^test02/", test02), url("^test03/", test03), ],None,None) ), ]
這種方法也能夠進行分發,注意:url第二個參數必定要是元組,元組的後兩個參數爆出None
固然,url中test01也能再加元組進行二級分發
被包含的URLconf 會收到來自父URLconf 捕獲的任何參數,因此下面的例子是合法的:
# 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), ]
在上面的例子中,捕獲的"username"
變量將被如期傳遞給include()指向的URLconf。
URLconfs 具備一個鉤子,讓你傳遞一個Python 字典做爲額外的參數傳遞給視圖函數。
django.conf.urls.url()
函數能夠接收一個可選的第三個參數,它是一個字典,表示想要傳遞給視圖函數的額外關鍵字參數。
像這樣:
from django.conf.urls import url from . import views urlpatterns = [ url(r'^blog/(?P<year>[0-9]{4})/$', views.year_archive, {'foo': 'bar'}), ]
在這個例子中,對於/blog/2005/
請求,Django 將調用views.year_archive(request, year='2005', foo='bar')
。這個技術在syndication framework 中使用,來傳遞元數據和選項給視圖。
處理衝突
URL 模式捕獲的命名關鍵字參數和在字典中傳遞的額外參數有可能具備相同的名稱。 當這種狀況發生時,將使用字典中的參數而不是URL 中捕獲的參數。
include()
相似地,你能夠傳遞額外的選項給include()
。 當你傳遞額外的選項給include()
時,被包含的URLconf 的每一 行將被傳遞這些額外的選項。
例如,下面兩個URLconf 設置功能上徹底相同:
設置一:
# main.py from django.conf.urls import include, url urlpatterns = [ url(r'^blog/', include('inner'), {'blogid': 3}), ] # inner.py from django.conf.urls import url from mysite import views urlpatterns = [ url(r'^archive/$', views.archive), url(r'^about/$', views.about), ]
設置二:
# main.py from django.conf.urls import include, url from mysite import views urlpatterns = [ url(r'^blog/', include('inner')), ] # inner.py from django.conf.urls import url urlpatterns = [ url(r'^archive/$', views.archive, {'blogid': 3}), url(r'^about/$', views.about, {'blogid': 3}), ]
注意,額外的選項將永遠傳遞給被包含的URLconf 中的每一行,不管該行的視圖其實是否定爲這些選項纔會生效。 因爲這個緣由,該技術只有當你肯定被包含的URLconf 中的每一個視圖都接收你傳遞給它們的額外的選項時纔有價值。
咱們在html或者view函數中常常能用到url,可是這些url一旦寫死了,若是路徑改了,這些寫死的url全廢了,所有都要改,關鍵還不必定能改全,因此就須要一種方式來動態的獲取url,不去寫死url,這種方式就叫url反向解析
在須要URL 的地方,對於不一樣層級,Django 提供不一樣的工具用於URL 反查:
反向解析實例
url(r'^home', views.home, name='home'), # 給個人url匹配模式起名爲 home url(r'^index/(\d*)', views.index, name='index'), # 給個人url匹配模式起名爲index
這樣:
在模板裏面能夠這樣引用:
{% url 'home' %}
在views函數中能夠這樣引用:
from django.urls import reverse reverse("index", args=("2018", ))
上面都是url不帶參數的時候,下面有帶參數時如何使用
實例
from django.conf.urls import url from . import views urlpatterns = [ #... url(r'^articles/([0-9]{4})/$', views.year_archive, name='news-year-archive'), #... ]
根據這裏的設計,某一年nnnn對應的歸檔的URL是/articles/nnnn/
。
<a href="{% url 'news-year-archive' 2012 %}">2012 Archive</a> {# Or with the year in a template context variable: #} <ul> {% for yearvar in year_list %} <li><a href="{% url 'news-year-archive' yearvar %}">{{ yearvar }} Archive</a></li> {% endfor %} </ul>
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,)))
若是出於某種緣由決定按年歸檔文章發佈的URL應該調整一下,那麼你將只須要修改URLconf 中的內容。
當url路徑帶參數時,reverse函數經過接受args或者kwargs={「year」: 2018}這種來傳遞參數,而模板中直接在url neme 後面接參數值便可
即便不一樣的APP使用相同的URL名稱,URL的命名空間模式也可讓你惟一反轉命名的URL。
舉個例子:
project中的urls.py
from django.conf.urls import url, include urlpatterns = [ url(r'^app01/', include('app01.urls', namespace='app01')), url(r'^app02/', include('app02.urls', namespace='app02')), ]
app01中的urls.py
from django.conf.urls import url from app01 import views app_name = 'app01' urlpatterns = [ url(r'^(?P<pk>\d+)/$', views.detail, name='detail') ]
app02中的urls.py
from django.conf.urls import url from app02 import views app_name = 'app02' urlpatterns = [ url(r'^(?P<pk>\d+)/$', views.detail, name='detail') ]
如今,個人兩個app中 url名稱重複了,我反轉URL的時候就能夠經過命名空間的名稱獲得我當前的URL。
語法:
'命名空間名稱:URL名稱'
模板中使用:
{% url 'app01:detail' pk=12 pp=99 %}
views中的函數中使用
v = reverse('app01:detail', kwargs={'pk':11})
這樣即便app中URL的命名相同,我也能夠反轉獲得正確的URL了。