1. 程式人生 > >django URL路由基礎

django URL路由基礎

URL是Web服務的入口,使用者通過瀏覽器傳送過來的任何請求,都是傳送到一個指定的URL地址,然後被響應。

在Django專案中編寫路由,就是向外暴露我們接收哪些URL的請求,除此之外的任何URL都不被處理,也沒有返回。通俗地理解,不恰當的形容,URL路由是你的Web服務對外暴露的API。

Django奉行DRY主義,提倡使用簡潔、優雅的URL,沒有.php.cgi這種字尾,更不會單獨使用0、2097、1-1-1928、00這樣無意義的東西,讓你隨心所欲設計你的URL,不受框架束縛。

一、概述

URL路由在Django專案中的體現就是urls.py檔案,這個檔案可以有很多個,但絕對不會在同一目錄下。實際上Django提倡專案有個根urls.py

,各app下分別有自己的一個urls.py,既集中又分治,是一種解耦的模式。

隨便新建一個Django專案,預設會自動為我們建立一個/project_name/urls.py檔案,並且自動包含下面的內容,這就是專案的根URL:

"""mysite URL Configuration

The `urlpatterns` list routes URLs to views. For more information please see:
    https://docs.djangoproject.com/en/1.11/topics/http/urls/
Examples:
Function views
 1. Add an import: from my_app import views  2. Add a URL to urlpatterns: url(r'^$', views.home, name='home') Class-based views  1. Add an import: from other_app.views import Home  2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home') Including another URLconf  1. Import the include() function: from django.conf.urls import url, include  2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls')) """ from django.conf.urls import url from django.contrib import admin urlpatterns = [ url(r'^admin/', admin.site.urls), ] 

前面一堆幫助性的文字,我們不用管,關鍵是預設匯入了url和admin,然後有一條指向admin後臺的url路徑。

我們自己要編寫的url路由,基本也是這個套路。

二、Django如何處理請求

當用戶請求一個頁面時,Django根據下面的邏輯執行操作:

  1. 決定要使用的根URLconf模組。通常,這是ROOT_URLCONF設定的值,但是如果傳入的HttpRequest物件具有urlconf屬性(由中介軟體設定),則其值將被用於代替ROOT_URLCONF設定。通俗的講,就是你可以自定義專案入口url是哪個檔案!
  2. 載入該模組並尋找可用的urlpatterns。 它是django.conf.urls.url()
    例項的一個列表。
  3. 依次匹配每個URL模式,在與請求的URL相匹配的第一個模式停下來。也就是說,url匹配是從上往下的短路操作,所以url在列表中的位置非常關鍵。
  4. 匯入並呼叫匹配行中給定的檢視,該檢視是一個簡單的Python函式(被稱為檢視函式),或基於類的檢視。 檢視將獲得如下引數:
    1. 一個HttpRequest 例項。
    2. 如果匹配的正則表示式返回了沒有命名的組,那麼正則表示式匹配的內容將作為位置引數提供給檢視。
    3. 關鍵字引數由正則表示式匹配的命名組組成,但是可以被django.conf.urls.url()的可選引數kwargs覆蓋。
  5. 如果沒有匹配到正則表示式,或者過程中丟擲異常,將呼叫一個適當的錯誤處理檢視。

三、簡單示例

下面是一個簡單的 URLconf:

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), ] 

我們要編寫的就是上面urlpatterns列表中的一條條url,每條url,都是urlpatterns列表的一個元素。先後順序有重要關係,不能隨意擺放。最後一條的末尾建議新增一個逗號。

urlpatterns中的每條正則表示式在第一次訪問時被自動編譯,因此其匹配速度是非常快的。

注意:

  • 若要從URL中捕獲一個值,只需要在它周圍放置一對圓括號。
  • 不需要新增前導的反斜槓,因為每個URL都有。 例如,應該是^articles而不是^/articles
  • 每個正則表示式前面的'r'是可選的但是建議加上。它告訴Python這個字串是“原始的” —— 字串中任何字元都不應該轉義。

根據上面的urlconf,下面是一些請求的例子,以及它們將匹配到的url:

  • /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中的值並以位置引數的形式傳遞給檢視。

可以使用命名的正則表示式組來捕獲URL中的值並以關鍵字引數傳遞給檢視。

在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匹配請求URL中的哪些部分

請求的URL被看做是一個普通的Python字串,URLconf在其上查詢並匹配。進行匹配時將不包括GET或POST請求方式的引數以及域名。

例如,在https://www.example.com/myapp/的請求中,URLconf將查詢myapp/

https://www.example.com/myapp/?page=3的請求中,URLconf也將查詢myapp/

URLconf不檢查使用何種HTTP請求方法,所有請求方法POST、GET、HEAD等都將路由到同一個URL的同一個檢視。在檢視中,才根據具體請求方法的不同,進行不同的處理。

六、URL中捕獲的引數為字串型別

每個捕獲的引數都作為一個普通的Python字串傳遞給檢視,即便被捕獲的‘100’看起來像個整數,但實際上是個字串‘100’。 例如,下面這行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時,或者當丟擲一個異常時,將呼叫一個錯誤處理檢視。錯誤檢視包括400、403、404和500,分別表示請求錯誤、拒絕服務、頁面不存在和伺服器錯誤。它們分別位於:

  • handler400 —— django.conf.urls.handler400。
  • handler403 —— django.conf.urls.handler403。
  • handler404 —— django.conf.urls.handler404。
  • handler500 —— django.conf.urls.handler500。

這些值可以在根URLconf中設定。在其它app中的二級URLconf中設定這些變數無效。

Django有內建的HTML模版,用於返回錯誤頁面給使用者,但是這些403,404頁面實在醜陋,通常我們都自定義錯誤頁面。

首先,在根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), ] # 增加的條目 handler400 = views.bad_request handler403 = views.permission_denied handler404 = views.page_not_found handler500 = views.page_error 

然後在,views.py檔案中增加四個處理檢視:

def page_not_found(request):
    return render(request, '404.html') def page_error(request): return render(request, '500.html') def permission_denied(request): return render(request, '403.html') def bad_request(request): return render(request, '400.html') 

再根據自己的需求,建立404.html、400.html等四個頁面檔案,就可以了。