URL是Web服務(wù)的入口含鳞,用戶通過瀏覽器發(fā)送過來的任何請求付呕,都是發(fā)送到一個指定的URL地址宛篇,然后被響應(yīng)娃磺。在Django項目中編寫路由,就是向外暴露我們接收哪些URL的請求叫倍,除此之外的任何URL都不被處理偷卧,也沒有返回。通俗地理解吆倦,不恰當?shù)男稳萏睿琔RL路由是你的Web服務(wù)對外暴露的API。Django奉行DRY主義蚕泽,提倡使用簡潔晌梨、優(yōu)雅的URL。
1. 概述
要設(shè)計應(yīng)用程序的URL,可以創(chuàng)建一個非正式的稱為URLconf(URL配置)的Python模塊仔蝌。此模塊是純Python代碼泛领,是URL路徑表達式與Python函數(shù)(您的視圖)之間的映射。該映射可以根據(jù)需要縮短或縮短敛惊。它可以引用其他映射渊鞋。并且,因為它是純Python代碼豆混,所以它可以動態(tài)構(gòu)造篓像。Django還提供了一種根據(jù)活動語言翻譯URL的方法。
2. django如何處理請求
當用戶請求一個頁面時皿伺,Django根據(jù)下面的邏輯執(zhí)行操作:
1.決定要使用的根URLconf模塊。通常盒粮,這是ROOT_URLCONF設(shè)置的值鸵鸥,但是如果傳入的HttpRequest對象具有urlconf屬性(由中間件設(shè)置),則其值將被用于代替ROOT_URLCONF設(shè)置丹皱。通俗的講妒穴,就是你可以自定義項目入口url是哪個文件!
2.加載該模塊并尋找可用的urlpatterns摊崭。?它是django.conf.urls.url()實例的一個列表讼油。
3.依次匹配每個URL模式,在與請求的URL相匹配的第一個模式停下來呢簸。也就是說矮台,url匹配是從上往下的短路操作,所以url在列表中的位置非常關(guān)鍵根时。
4.導(dǎo)入并調(diào)用匹配行中給定的視圖瘦赫,該視圖是一個簡單的Python函數(shù)(被稱為視圖函數(shù)),或基于類的視圖。 視圖將獲得如下參數(shù):
(1)一個HttpRequest 實例蛤迎。
(2)如果匹配的正則表達式返回了沒有命名的組确虱,那么正則表達式匹配的內(nèi)容將作為位置參數(shù)提供給視圖。
(3)關(guān)鍵字參數(shù)由正則表達式匹配的命名組組成替裆,但是可以被django.conf.urls.url()的可選參數(shù)kwargs覆蓋校辩。
5.如果沒有匹配到正則表達式,或者過程中拋出異常辆童,將調(diào)用一個適當?shù)腻e誤處理視圖宜咒。
3. 轉(zhuǎn)換器
3.1 path轉(zhuǎn)換器
在django2.0 以上的版本中,默認使用的是path轉(zhuǎn)換器:
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),
]
注意:
1.要捕獲一段url中的值胸遇,需要使用尖括號荧呐,而不是之前的圓括號;
2.可以轉(zhuǎn)換捕獲到的值為指定類型,比如例子中的int倍阐。默認情況下概疆,捕獲到的結(jié)果保存為字符串類型,不包含/這個特殊字符峰搪;
3.匹配模式的最開頭不需要添加/岔冀,因為默認情況下,每個url都帶一個最前面的/概耻,既然大家都有的部分使套,就不用浪費時間特別寫一個了。
匹配例子:
●/articles/2005/03/ 將匹配第三條鞠柄,并調(diào)用views.month_archive(request, year=2005, month=3)侦高;
●/articles/2003/匹配第一條,并調(diào)用views.special_case_2003(request)厌杜;
●/articles/2003將一條都匹配不上奉呛,因為它最后少了一個斜杠,而列表中的所有模式中都以斜杠結(jié)尾夯尽;
●/articles/2003/03/building-a-django-site/ 將匹配最后一個瞧壮,并調(diào)用views.article_detail(request, year=2003, month=3, slug="building-a-django-site"
默認情況下,Django內(nèi)置下面的路徑轉(zhuǎn)換器:
●str:匹配任何非空字符串匙握,但不含斜杠/咆槽,如果你沒有專門指定轉(zhuǎn)換器,那么這個是默認使用的圈纺;
●int:匹配0和正整數(shù)秦忿,返回一個int類型
●slug:可理解為注釋、后綴赠堵、附屬等概念小渊,是url拖在最后的一部分解釋性字符。該轉(zhuǎn)換器匹配任何ASCII字符以及連接符和下劃線茫叭,比如’ building-your-1st-django-site‘酬屉;
●uuid:匹配一個uuid格式的對象。為了防止沖突揍愁,規(guī)定必須使用破折號呐萨,所有字母必須小寫,例如’075194d3-6885-417e-a8a8-6c931e272f00‘ 莽囤。返回一個UUID對象谬擦;
●path:匹配任何非空字符串,重點是可以包含路徑分隔符’/‘朽缎。這個轉(zhuǎn)換器可以幫助你匹配整個url而不是一段一段的url字符串惨远。
3.2 re_path轉(zhuǎn)換器(老版的url)
Django2.0的url雖然改‘配置’了谜悟,但它依然向老版本兼容。而這個兼容的辦法北秽,就是用re_path()方法代替path()方法葡幸。re_path()方法在骨子里,根本就是以前的url()方法贺氓,只不過導(dǎo)入的位置變了蔚叨。
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),
]
與path()方法不同的在于兩點:
●year中匹配不到10000等非四位數(shù)字,這是正則表達式?jīng)Q定的
●傳遞給視圖的所有參數(shù)都是字符串類型辙培。而不像path()方法中可以指定轉(zhuǎn)換成某種類型蔑水。在視圖中接收參數(shù)時一定要小心。
3.3 有名分組
上面的示例使用簡單的扬蕊、沒有命名的正則表達式組(通過圓括號)來捕獲URL 中的值并以位置 參數(shù)傳遞給視圖搀别。在更高級的用法中,可以使用命名的正則表達式組來捕獲URL 中的值并以關(guān)鍵字 參數(shù)傳遞給視圖厨相。 在Python 正則表達式中领曼,命名正則表達式組的語法是(?Ppattern),其中name 是組的名稱蛮穿,pattern 是要匹配的模式。 下面是以上URLconf 使用命名組的重寫:
from django.urls import path,re_path
from app01 import views
urlpatterns = [
re_path(r'^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<day>[0-9]{2})/$', views.article_detail),
]
這個實現(xiàn)與前面的示例完全相同毁渗,只有一個細微的差別:捕獲的值作為關(guān)鍵字參數(shù)而不是位置參數(shù)傳遞給視圖函數(shù)践磅。例如:
/articles/2005/03/ 請求將調(diào)用views.month_archive(request, year='2005', month='03')函數(shù),而不是views.month_archive(request, '2005', '03')灸异。
/articles/2003/03/03/ 請求將調(diào)用函數(shù)views.article_detail(request, year='2003', month='03', day='03')府适。
3.4 路由分發(fā)
通常,我們會在每個app里肺樟,各自創(chuàng)建一個urls.py路由模塊檐春,然后從根路由出發(fā),將app所屬的url請求么伯,全部轉(zhuǎn)發(fā)到相應(yīng)的urls.py模塊中疟暖。
例如,下面是Django網(wǎng)站本身的URLconf節(jié)選田柔。 它包含許多其它URLconf:
from django.conf.urls import include, url
urlpatterns = [
? ? # ... 忽略 ...
? ? re_path(r'^community/', include('django_website.aggregator.urls')),
? ? re_path(r'^contact/', include('django_website.contact.urls')),
? ? # ... 忽略 ...
]
路由轉(zhuǎn)發(fā)使用的是include()方法俐巴,需要提前導(dǎo)入,它的參數(shù)是轉(zhuǎn)發(fā)目的地路徑的字符串硬爆,路徑以圓點分割欣舵。
注意,這個例子中的正則表達式?jīng)]有包含$(字符串結(jié)束匹配符)缀磕,但是包含一個末尾的斜杠缘圈。 每當Django 遇到include()(來自django.conf.urls.include())時劣光,它會去掉URL中匹配的部分并將剩下的字符串發(fā)送給include的URLconf做進一步處理,也就是轉(zhuǎn)發(fā)到二級路由去糟把。
另外一種轉(zhuǎn)發(fā)其它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 = [
? ? re_path(r'^reports/$', credit_views.report),
? ? re_path(r'^reports/(?P<id>[0-9]+)/$', credit_views.report),
? ? re_path(r'^charge/$', credit_views.charge),
]
urlpatterns = [
? ? re_path(r'^$', main_views.homepage),
? ? re_path(r'^help/', include('apps.help.urls')),
? ? re_path(r'^credit/', include(extra_patterns)),
]
在此示例中糊饱,/credit/reports/URL將由credit_views.report()視圖處理垂寥。這種做法,相當于把二級路由模塊內(nèi)的代碼寫到根路由模塊里一起了另锋,不是很推薦滞项。
再看下面的URLconf:
from django.conf.urls import url
from . import views
urlpatterns = [
? ? re_path(r'^(?P<page_slug>[\w-]+)-(?P<page_id>\w+)/history/$', views.history),
? ? re_path(r'^(?P<page_slug>[\w-]+)-(?P<page_id>\w+)/edit/$', views.edit),
? ? re_path(r'^(?P<page_slug>[\w-]+)-(?P<page_id>\w+)/discuss/$', views.discuss),
? ? re_path(r'^(?P<page_slug>[\w-]+)-(?P<page_id>\w+)/permissions/$', views.permissions),
]
上面的路由寫得不好,我們可以改進它夭坪,只需要聲明共同的路徑前綴一次文判,并將后面的部分分組轉(zhuǎn)發(fā):
from django.conf.urls import include, url
from . import views
urlpatterns = [
? ? re_path(r'^(?P<page_slug>[\w-]+)-(?P<page_id>\w+)/', include([
? ? ? ? re_path(r'^history/$', views.history),
? ? ? ? re_path(r'^edit/$', views.edit),
? ? ? ? re_path(r'^discuss/$', views.discuss),
? ? ? ? re_path(r'^permissions/$', views.permissions),
? ? ])),
]
3.5 反向解析
在使用Django 項目時,一個常見的需求是獲得URL 的最終形式室梅,以用于嵌入到生成的內(nèi)容中(視圖中和顯示給用戶的URL等)或者用于處理服務(wù)器端的導(dǎo)航(重定向等)戏仓。人們強烈希望不要硬編碼這些URL(費力、不可擴展且容易產(chǎn)生錯誤)或者設(shè)計一種與URLconf 毫不相關(guān)的專門的URL 生成機制亡鼠,因為這樣容易導(dǎo)致一定程度上產(chǎn)生過期的URL赏殃。 在需要URL 的地方,對于不同層級间涵,Django 提供不同的工具用于URL 反查:
1.在模板中:使用url 模板標簽仁热。
2.在Python 代碼中:使用from django.urls import reverse()函數(shù) urls.py:
from django.conf.urls import url
from . import views
urlpatterns = [
#...
re_path(r'^articles/([0-9]{4})/$', views.year_archive, name='news-year-archive'),
#...
]
在模板中:
<a href="{% url 'news-year-archive' 2012 %}">2012 Archive</a>
<ul>
{% for yearvar in year_list %}
<li><a href="{% url 'news-year-archive' yearvar %}">{{ yearvar }} Archive</a></li>
{% endfor %}
</ul>
在python中:
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,))) # 同redirect("/path/")
當命名你的URL 模式時,請確保使用的名稱不會與其它應(yīng)用中名稱沖突勾哩。如果你的URL 模式叫做comment抗蠢,而另外一個應(yīng)用中也有一個同樣的名稱,當你在模板中使用這個名稱的時候不能保證將插入哪個URL思劳。在URL 名稱中加上一個前綴迅矛,比如應(yīng)用的名稱,將減少沖突的可能潜叛。我們建議使用myapp-comment 而不是comment秽褒。
3.6 名稱空間
命名空間(英語:Namespace)是表示標識符的可見范圍。一個標識符可在多個命名空間中定義钠导,它在不同命名空間中的含義是互不相干的震嫉。這樣,在一個新的命名空間中可定義任何標識符牡属,它們不會與任何已有的標識符發(fā)生沖突票堵,因為已有的定義都處于其它命名空間中。 由于name沒有作用域逮栅,Django在反解URL時悴势,會在項目全局順序搜索窗宇,當查找到第一個name指定URL時,立即返回 我們在開發(fā)項目時特纤,會經(jīng)常使用name屬性反解出URL军俊,當不小心在不同的app的urls中定義相同的name時,可能會導(dǎo)致URL反解錯誤捧存,為了避免這種事情發(fā)生粪躬,引入了命名空間。
project的urls.py:
urlpatterns = [
re_path(r'^admin/', admin.site.urls),
re_path(r'^app01/', include("app01.urls",namespace="app01")),
re_path(r'^app02/', include("app02.urls",namespace="app02")),
]
app01.urls:
urlpatterns = [
re_path(r'^index/', index,name="index"),
]
app02.urls:
urlpatterns = [
? ? re_path(r'^index/', index,name="index"),
]
app01.views
from django.core.urlresolvers import reverse
def index(request):
? ? return HttpResponse(reverse("app01:index"))
app02.views
from django.core.urlresolvers import reverse
def index(request):
? ? return HttpResponse(reverse("app02:index"))
附:
^ 指定起始字符或字符串昔穴,放進【】代表否定
$ 指定終止字符
/ 對應(yīng)原來的字符
【镰官。。吗货∮具耄】 括號中表示一個字符的格式設(shè)置
\d 任何一個數(shù)字字符
\D 非數(shù)字的字符
\w 任何一個字符【a-zA-Z0-9】
\W 非任何一個字符【^a-zA-Z0-9】
? 代表前面一個字符樣式可以重復(fù)出現(xiàn)0次或1次
* 可以重復(fù)出現(xiàn)0次或0次以上
+ 可以重復(fù)出現(xiàn)1次或1次以上
{m} 大括號中間數(shù)字m,代表前一個字符出現(xiàn)m次
{m,n} 代表前一個字符可以出現(xiàn)m-n次
| 或宙搬,
(...) 小括號中間匹配笨腥,或取出成為一個參數(shù)
(?P<name>) 同上勇垛,但是指定此參數(shù)名稱那么