干凈,優(yōu)雅的URL方案是高質(zhì)量Web應(yīng)用程序中的重要細節(jié)。Django允許您根據(jù)需要設(shè)計URL,而無框架限制。
萬維網(wǎng)創(chuàng)建者蒂姆·伯納斯-李(Tim Berners-Lee)的文章“ Cool URIs not not change”中有關(guān)為什么URL應(yīng)該干凈和可用的出色論據(jù),請參見。
要設(shè)計應(yīng)用程序的URL,您可以創(chuàng)建一個非正式地稱為URLconf(URL配置)的Python模塊 。該模塊是純Python代碼,并且是URL路徑表達式到Python函數(shù)(您的視圖)之間的映射。
該映射可以根據(jù)需要短或長。它可以引用其他映射。而且,由于它是純Python代碼,因此可以動態(tài)構(gòu)建。
Django還提供了一種根據(jù)活動語言翻譯URL的方法。有關(guān)更多信息,請參見國際化文檔。
當(dāng)用戶從您的Django支持的網(wǎng)站請求頁面時,系統(tǒng)將使用以下算法來確定要執(zhí)行的Python代碼:
這是一個示例URLconf:
from django.urls import path
?
from . import views
?
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),
]
筆記:
請求示例:
默認情況下,以下路徑轉(zhuǎn)換器可用:
對于更復(fù)雜的匹配要求,您可以定義自己的路徑轉(zhuǎn)換器。
轉(zhuǎn)換器是包含以下內(nèi)容的類:
例如:
class FourDigitYearConverter:
regex = '[0-9]{4}'
?
def to_python(self, value):
return int(value)
?
def to_url(self, value):
return '%04d' % value
使用register_converter()以下命令在URLconf中注冊自定義轉(zhuǎn)換器類 :
from django.urls import path, register_converter
?
from . import converters, views
?
register_converter(converters.FourDigitYearConverter, 'yyyy')
?
urlpatterns = [
path('articles/2003/', views.special_case_2003),
path('articles/<yyyy:year>/', views.year_archive),
...
]
如果路徑和轉(zhuǎn)換器語法不足以定義URL模式,則還可以使用正則表達式。為此,請使用 re_path()代替path()。
在Python正則表達式中,命名正則表達式組的語法為(?Ppattern),其中name是組的名稱,并且 pattern是匹配的某種模式。
這是前面的示例URLconf,使用正則表達式重寫:
from django.urls import path, re_path
?
from . import views
?
urlpatterns = [
path('articles/2003/', views.special_case_2003),
re_path(r'^articles/(?P<year>[0-9]{4})/$', views.year_archive),
re_path(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/$', views.month_archive),
re_path(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/(?P<slug>[\w-]+)/$', views.article_detail),
]
這可以完成與上一個示例大致相同的操作,除了:
當(dāng)從使用切換為使用path(), re_path()反之亦然時,特別重要的是要注意視圖參數(shù)的類型可能會更改,因此您可能需要調(diào)整視圖。
除了命名組語法(例如)之外(?P[0-9]{4}),您還可以使用較短的未命名組(例如)([0-9]{4})。
不建議特別使用此用法,因為這樣可以更輕松地在匹配的預(yù)期含義和視圖的參數(shù)之間意外引入錯誤。
無論哪種情況,建議在給定的正則表達式中僅使用一種樣式。當(dāng)兩種樣式混合使用時,任何未命名的組都會被忽略,只有命名的組才會傳遞給視圖函數(shù)。
正則表達式允許嵌套參數(shù),而Django會解析它們并將其傳遞給視圖。反轉(zhuǎn)時,Django將嘗試填寫所有外部捕獲的參數(shù),而忽略任何嵌套的捕獲參數(shù)。考慮以下URL模式,這些URL模式可以選擇采用page參數(shù):
from django.urls import re_path
?
urlpatterns = [
re_path(r'^blog/(page-(\d+)/)?$', blog_articles), # bad
re_path(r'^comments/(?:page-(?P<page_number>\d+)/)?$', comments), # good
]
兩種模式都使用嵌套參數(shù),并將解析:例如, blog/page-2/將導(dǎo)致與匹配blog_articles兩個位置參數(shù):page-2/和2。的第二個模式 comments將comments/page-2/與關(guān)鍵字參數(shù) page_number設(shè)置為2 匹配。在這種情況下,外部參數(shù)是一個非捕獲參數(shù)(?:...)。
該blog_articles視圖需要最外層捕獲的參數(shù)被反轉(zhuǎn),page-2/或者在這種情況下不需要參數(shù),而視圖 comments可以不帶參數(shù)也沒有值而被反轉(zhuǎn)page_number。
嵌套的捕獲參數(shù)在視圖參數(shù)和URL之間建立了牢固的耦合,如下所示blog_articles:視圖接收部分URL(page-2/),而不是僅接收視圖感興趣的值。這種反轉(zhuǎn)在反轉(zhuǎn)時更為明顯,因為反轉(zhuǎn)視圖,我們需要傳遞該URL而不是頁碼。
根據(jù)經(jīng)驗,當(dāng)正則表達式需要參數(shù)但視圖將其忽略時,僅捕獲視圖需要使用的值,并使用非捕獲參數(shù)。
URLconf按照正常的Python字符串搜索請求的URL。這不包括GET或POST參數(shù)或域名。
例如,在對的請求中https://www.example.com/myapp/,URLconf將尋找myapp/。
在請求中https://www.example.com/myapp/?page=3,URLconf將尋找myapp/。
URLconf不會查看請求方法。換句話說,所有的請求方法- ,,POST 等-將被路由到相同的URL相同的功能。GET``HEAD
一個方便的技巧是為視圖的參數(shù)指定默認參數(shù)。這是一個示例URLconf和視圖:
# URLconf
from django.urls import path
?
from . import views
?
urlpatterns = [
path('blog/', views.page),
path('blog/page<int:num>/', 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中捕獲任何內(nèi)容。如果第一個模式匹配,該page()函數(shù)將使用它的默認參數(shù)num,1。如果第二個模式匹配, page()將使用num捕獲的任何值。
中的每個正則表達式urlpatterns都是在首次訪問時進行編譯。這使系統(tǒng)運行起來非??臁?/p>
urlpatterns應(yīng)該是一個序列的path() 和/或re_path()實例。
當(dāng)Django無法找到所請求URL的匹配項或引發(fā)異常時,Django會調(diào)用錯誤處理視圖。
這些情況下使用的視圖由四個變量指定。它們的默認值足以滿足大多數(shù)項目的需要,但可以通過覆蓋其默認值來進行進一步的自定義。
有關(guān)完整的詳細信息,請參見有關(guān)自定義錯誤視圖的文檔。
可以在您的根URLconf中設(shè)置這些值。在任何其他URLconf中設(shè)置這些變量將無效。
值必須是可調(diào)用的,或者是表示視圖的完整Python導(dǎo)入路徑的字符串,應(yīng)該調(diào)用該視圖來處理當(dāng)前的錯誤情況。
變量是:
在任何時候,您urlpatterns都可以“包括”其他URLconf模塊。實質(zhì)上,這會將“ URL”“植根”在其他URL之下。
例如,這是Django網(wǎng)站 本身的URLconf的摘錄。它包括許多其他URLconf:
from django.urls import include, path
?
urlpatterns = [
# ... snip ...
path('community/', include('aggregator.urls')),
path('contact/', include('contact.urls')),
# ... snip ...
]
每當(dāng)Django遇到時include(),它都會截斷直到該時間點匹配的URL的任何部分,并將剩余的字符串發(fā)送到包含的URLconf中以進行進一步處理。
另一種可能性是通過使用path()實例列表包括其他URL模式 。例如,考慮以下URLconf:
from django.urls import include, path
?
from apps.main import views as main_views
from credit import views as credit_views
?
extra_patterns = [
path('reports/', credit_views.report),
path('reports/<int:id>/', credit_views.report),
path('charge/', credit_views.charge),
]
?
urlpatterns = [
path('', main_views.homepage),
path('help/', include('apps.help.urls')),
path('credit/', include(extra_patterns)),
]
在此示例中,/credit/reports/URL將由credit_views.report()Django視圖處理 。
這可用于從URLconf中刪除重復(fù)使用單個模式前綴的冗余。例如,考慮以下URLconf:
from django.urls import path from . import views urlpatterns = [ path('<page_slug>-<page_id>/history/', views.history), path('<page_slug>-<page_id>/edit/', views.edit), path('<page_slug>-<page_id>/discuss/', views.discuss), path('<page_slug>-<page_id>/permissions/', views.permissions), ]
我們可以通過只聲明一次公共路徑前綴并對不同的后綴進行分組來改善這一點:
from django.urls import include, path from . import views urlpatterns = [ path('<page_slug>-<page_id>/', include([ path('history/', views.history), path('edit/', views.edit), path('discuss/', views.discuss), path('permissions/', views.permissions), ])), ]
包含的URLconf從父URLconfs接收任何捕獲的參數(shù),因此以下示例有效:
# In settings/urls/main.py from django.urls import include, path urlpatterns = [ path('<username>/blog/', include('foo.urls.blog')), ] # In foo/urls/blog.py from django.urls import path from . import views urlpatterns = [ path('', views.blog.index), path('archive/', views.blog.archive), ]
在上面的示例中,捕獲的"username"變量按預(yù)期傳遞給包含的URLconf。
URLconfs有一個鉤子,可讓您將額外的參數(shù)作為Python字典傳遞給視圖函數(shù)。
該path()函數(shù)可以使用可選的第三個參數(shù),該參數(shù)應(yīng)該是傳遞給view函數(shù)的額外關(guān)鍵字參數(shù)的字典。
例如:
from django.urls import path from . import views urlpatterns = [ path('blog/<int:year>/', views.year_archive, {'foo': 'bar'}), ]
在此示例中,對于的請求/blog/2005/,Django將調(diào)用 。views.year_archive(request, year=2005, foo='bar')
該技術(shù)在 聯(lián)合框架中用于將元數(shù)據(jù)和選項傳遞給視圖。
處理沖突
URL模式可能會捕獲命名的關(guān)鍵字參數(shù),并在其額外參數(shù)字典中傳遞具有相同名稱的參數(shù)。發(fā)生這種情況時,將使用字典中的參數(shù)代替URL中捕獲的參數(shù)。
同樣,您可以將額外選項傳遞給include(),所包含的URLconf中的每一行都將傳遞額外選項。
例如,這兩個URLconf集在功能上是相同的:
設(shè)置一:
# main.py from django.urls import include, path urlpatterns = [ path('blog/', include('inner'), {'blog_id': 3}), ] # inner.py from django.urls import path from mysite import views urlpatterns = [ path('archive/', views.archive), path('about/', views.about), ]
設(shè)置二:
# main.py from django.urls import include, path from mysite import views urlpatterns = [ path('blog/', include('inner')), ] # inner.py from django.urls import path urlpatterns = [ path('archive/', views.archive, {'blog_id': 3}), path('about/', views.about, {'blog_id': 3}), ]
請注意,無論行的視圖是否實際接受這些選項,額外的選項將始終傳遞到所包含的URLconf中的每一行。因此,僅當(dāng)您確定所包含的URLconf中的每個視圖都接受要傳遞的額外選項時,此技術(shù)才有用。
在Django項目上進行工作時,通常需要獲取最終形式的URL,以嵌入生成的內(nèi)容(視圖和資產(chǎn)URL,向用戶顯示的URL等)或在服務(wù)器上處理導(dǎo)航流程側(cè)面(重定向等)
強烈希望避免對這些URL進行硬編碼(一種費力,不可擴展且易于出錯的策略)。同樣危險的是,設(shè)計臨時機制來生成與URLconf描述的設(shè)計平行的URL,這可能導(dǎo)致URL的生成隨著時間的推移而變得陳舊。
換句話說,需要一種DRY機制。除其他優(yōu)點外,它還允許URL設(shè)計的發(fā)展,而不必遍歷所有項目源代碼來搜索和替換過時的URL。
我們可以獲得URL的主要信息是負責(zé)處理它的視圖的標(biāo)識(例如名稱)。視圖參數(shù)的類型(位置,關(guān)鍵字)和值還必須包含在正確的URL查找中的其他信息。
Django提供了一個解決方案,使得URL映射器是URL設(shè)計的唯一存儲庫。您將其與URLconf一起提供,然后可以在兩個方向上使用它:
第一個是我們在上一節(jié)中討論的用法。第二種是所謂的URL反向解析,反向URL匹配,反向URL查找或簡稱URL反向。
Django提供了執(zhí)行URL反轉(zhuǎn)的工具,這些工具與需要URL的不同層相匹配:
再次考慮以下URLconf條目:
from django.urls import path from . import views urlpatterns = [ #... path('articles/<int:year>/', views.year_archive, name='news-year-archive'), #... ]
根據(jù)這種設(shè)計,對應(yīng)于年度歸檔文件的URL NNNN 是/articles//。
您可以使用以下模板代碼獲取它們:
<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>
或在Python代碼中:
from django.http import HttpResponseRedirect from django.urls import reverse def redirect_to_year(request): # ... year = 2006 # ... return HttpResponseRedirect(reverse('news-year-archive', args=(year,)))
如果出于某種原因決定更改發(fā)布年度文章存檔內(nèi)容的URL,則只需要更改URLconf中的條目即可。
在視圖具有一般性質(zhì)的某些情況下,URL和視圖之間可能存在多對一關(guān)系。對于這些情況,在反向URL時,視圖名稱并不是一個足夠好的標(biāo)識符。閱讀下一節(jié)以了解Django為此提供的解決方案。
為了執(zhí)行URL反向,您需要 像上面的示例一樣使用命名的URL模式。URL名稱使用的字符串可以包含您喜歡的任何字符。您不限于有效的Python名稱。
在命名URL模式時,請選擇不太可能與其他應(yīng)用程序的名稱沖突的名稱。如果調(diào)用URL模式,comment 而另一個應(yīng)用程序執(zhí)行相同的操作,則reverse()找到的URL 取決于項目urlpatterns列表中最后一個模式。
在您的URL名稱上添加前綴(可能源自應(yīng)用程序名稱(例如myapp-comment而不是comment)),可以減少發(fā)生沖突的機會。
如果要覆蓋視圖,可以故意選擇與另一個應(yīng)用程序相同的URL名稱。例如,一個常見的用例是覆蓋 LoginView。Django和大多數(shù)第三方應(yīng)用程序的某些部分假定此視圖具有名稱為的URL模式 login。如果你有一個自定義登錄查看,并給它的URL的名字login, reverse()會發(fā)現(xiàn)自定義視圖,只要它在 urlpatterns以后django.contrib.auth.urls包括(如果這是包含在所有)。
如果多個URL模式的參數(shù)不同,也可以使用相同的名稱。除URL名稱外,還要reverse() 匹配參數(shù)數(shù)量和關(guān)鍵字參數(shù)的名稱。
URL名稱空間允許您唯一地反向命名URL模式,即使不同的應(yīng)用程序使用相同的URL名稱。對于第三方應(yīng)用程序,始終使用命名空間的URL是一個好習(xí)慣(就像我們在本教程中所做的那樣)。同樣,如果部署了一個應(yīng)用程序的多個實例,它還允許您反向URL。換句話說,由于單個應(yīng)用程序的多個實例將共享命名URL,因此名稱空間提供了一種區(qū)分這些命名URL的方法。
對于特定站點,可以多次使用正確使用URL名稱空間的Django應(yīng)用程序。例如,django.contrib.admin 有一AdminSite類允許您 部署多個admin實例。在下一個示例中,我們將討論從教程在兩個不同位置部署民意調(diào)查應(yīng)用程序的想法,以便我們可以為兩個不同的受眾(作者和發(fā)布者)提供相同的功能。
URL名稱空間分為兩部分,都是字符串:
使用':'操作符指定以名稱分隔的URL 。例如,使用引用管理應(yīng)用程序的主索引頁面'admin:index'。這表示的命名空間'admin',以及的命名URL 'index'。
命名空間也可以嵌套。命名的URL 'sports:polls:index'將尋找'index'在命名空間中命名的模式,該模式'polls'本身是在頂級命名空間中定義的'sports'。
給定'polls:index'要解析的命名空間URL(例如)后,Django會將完全限定的名稱拆分為多個部分,然后嘗試以下查找:
如果存在嵌套的名稱空間,則對名稱空間的每個部分重復(fù)這些步驟,直到僅解析視圖名稱為止。然后,將視圖名稱解析為找到的名稱空間中的URL。
為了展示該解決方案的實際作用,請考慮polls本教程中應(yīng)用程序的兩個實例的示例:一個稱為'author-polls' ,一個稱為'publisher-polls'。假設(shè)我們已經(jīng)增強了該應(yīng)用程序,以便在創(chuàng)建和顯示民意測驗時考慮實例名稱空間。
的urls.py
from django.urls import include, path urlpatterns = [ path('author-polls/', include('polls.urls', namespace='author-polls')), path('publisher-polls/', include('polls.urls', namespace='publisher-polls')), ]
民調(diào)/的urls.py
from django.urls import path from . import views app_name = 'polls' urlpatterns = [ path('', views.IndexView.as_view(), name='index'), path('<int:pk>/', views.DetailView.as_view(), name='detail'), ... ]
使用此設(shè)置,可以進行以下查找:
如果還存在一個默認實例(即名為的實例)'polls',則唯一的更改就是沒有當(dāng)前實例(上面列表中的第二項)。在這種情況下,'polls:index' 它將解析為默認實例的索引頁,而不是最后一個在中聲明的實例urlpatterns。
可以通過兩種方式指定包含的URLconf的應(yīng)用程序名稱空間。
首先,您可以app_name在包含的URLconf模塊中設(shè)置與該urlpatterns屬性相同級別的屬性。您必須將實際模塊或?qū)υ撃K的字符串引用傳遞給include(),而不是其urlpatterns自身的列表。
民調(diào)/的urls.py
from django.urls import path from . import views app_name = 'polls' urlpatterns = [ path('', views.IndexView.as_view(), name='index'), path('<int:pk>/', views.DetailView.as_view(), name='detail'), ... ]
的urls.py
from django.urls import include, path urlpatterns = [ path('polls/', include('polls.urls')), ]
中定義的URL polls.urls將具有一個應(yīng)用程序名稱空間polls。
其次,您可以包括一個包含嵌入式名稱空間數(shù)據(jù)的對象。如果您include()列出path()或 re_path()實例,則該對象中包含的URL將被添加到全局名稱空間中。但是,您還可以include()包含一個包含以下內(nèi)容的2元組:
(<list of path()/re_path() instances>, <application namespace>)
例如:
from django.urls import include, path from . import views polls_patterns = ([ path('', views.IndexView.as_view(), name='index'), path('<int:pk>/', views.DetailView.as_view(), name='detail'), ], 'polls') urlpatterns = [ path('polls/', include(polls_patterns)), ]
這會將提名的URL模式包括到給定的應(yīng)用程序名稱空間中。
可以使用的namespace參數(shù) 指定實例名稱空間include()。如果未指定實例名稱空間,它將默認為包含的URLconf的應(yīng)用程序名稱空間。這意味著它將也是該名稱空間的默認實例。
詳細參考: https://docs.djangoproject.com/en/3.0/topics/http/urls/
更多建議: