Django路由控制URL詳解

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ù)名稱那么


?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末脖母,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子闲孤,更是在濱河造成了極大的恐慌镶奉,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件崭放,死亡現(xiàn)場離奇詭異,居然都是意外死亡鸽凶,警方通過查閱死者的電腦和手機币砂,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來玻侥,“玉大人决摧,你說我怎么就攤上這事〈绽迹” “怎么了掌桩?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長姑食。 經(jīng)常有香客問我波岛,道長,這世上最難降的妖魔是什么音半? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任则拷,我火速辦了婚禮贡蓖,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘煌茬。我一直安慰自己斥铺,他們只是感情好,可當我...
    茶點故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布坛善。 她就那樣靜靜地躺著晾蜘,像睡著了一般。 火紅的嫁衣襯著肌膚如雪眠屎。 梳的紋絲不亂的頭發(fā)上剔交,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天,我揣著相機與錄音组力,去河邊找鬼省容。 笑死,一個胖子當著我的面吹牛燎字,可吹牛的內(nèi)容都是我干的腥椒。 我是一名探鬼主播,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼候衍,長吁一口氣:“原來是場噩夢啊……” “哼笼蛛!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起蛉鹿,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤滨砍,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后妖异,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體惋戏,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年他膳,在試婚紗的時候發(fā)現(xiàn)自己被綠了响逢。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡棕孙,死狀恐怖舔亭,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情蟀俊,我是刑警寧澤钦铺,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站肢预,受9級特大地震影響矛洞,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜误甚,卻給世界環(huán)境...
    茶點故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一缚甩、第九天 我趴在偏房一處隱蔽的房頂上張望谱净。 院中可真熱鬧,春花似錦擅威、人聲如沸壕探。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽李请。三九已至,卻和暖如春厉熟,著一層夾襖步出監(jiān)牢的瞬間导盅,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工揍瑟, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留白翻,地道東北人。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓绢片,卻偏偏與公主長得像滤馍,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子底循,可洞房花燭夜當晚...
    茶點故事閱讀 44,577評論 2 353

推薦閱讀更多精彩內(nèi)容