Django學習筆記4

路由系統(從屬於視圖層)

Django如何處理請求

當一個用戶請求Django 站點的一個頁面,下面是Django 系統決定執行哪一個Python  url路徑對應代碼遵循的算法:html

  1. Django 決定要使用的根URLconf 模塊。(最外層根路徑,url從什麼地方開始匹配一般,這是ROOT_URLCONF設置的值,可是若是傳入的HttpRequest對象具備urlconf屬性(由中間件設置),則其值將被用於代替ROOT_URLCONF設置。
  2. Django 加載該Python 模塊並尋找可用的urlpatterns。 它是django.conf.urls.url() 實例的一個Python 列表。
  3. Django 依次匹配每一個URL 模式,在與請求的URL 匹配的第一個模式停下來。
  4. 一旦正則表達式匹配,Django將導入並調用給定的視圖,該視圖是一個簡單的Python函數(或基於類的class-based view)。 視圖將得到以下參數:
    • 一個HttpRequest 實例。
    • 若是匹配的正則表達式返回了沒有命名的組,那麼正則表達式匹配的內容將做爲位置參數提供給視圖。(位置參數命名組)
    • 關鍵字參數由正則表達式匹配的命名組組成,可是能夠被django.conf.urls.url()的可選參數kwargs覆蓋。(關鍵字參數命名組)
  5. 若是沒有匹配到正則表達式,或者若是過程當中拋出一個異常,Django 將調用一個適當的錯誤處理視圖。

url配置

對於高質量的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),
]

注:正則表達式

  • 若要從URL中捕獲一個值,只須要在它周圍放置一對圓括號(分組匹配)。
  • 不須要添加一個前導的反斜槓,由於每一個URL 都有。 例如,應該是^articles 而不是 ^/articles
  • 每一個正則表達式前面的'r' 是可選的可是建議加上。 它告訴Python 這個字符串是「原始的」 —— 字符串中任何字符都不該該轉義。
  • 是否開啓URL訪問地址後面不爲/跳轉至帶有/的路徑的配置項 -----------------------APPEND_SLASH=True算法

  • Django 2.0版本中的路由系統已經替換成下面的寫法
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 解析器使用的算法,針對正則表達式中的命名組和非命名組:

  1. 若是有命名參數,則使用這些命名參數,忽略非命名參數。
  2. 不然,它將以位置參數傳遞全部的非命名參數。

根據傳遞額外的選項給視圖函數(下文),這兩種狀況下,多餘的關鍵字參數也將傳遞給視圖。

URLconf匹配的位置

URLconf 在請求的URL 上查找,將它當作一個普通的Python 字符串。不包括GET和POST參數以及域名。

例如,http://www.example.com/myapp/ 請求中,URLconf 將查找myapp/。

在http://www.example.com/myapp/?page=3 請求中,URLconf 仍將查找myapp/

URLconf 不檢查請求的方法。換句話講,全部的請求方法 —— 同一個URL的POSTGETHEAD等等 —— 都將路由到相同的函數。

捕獲的參數老是字符串

每一個捕獲的參數都做爲一個普通的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 完整導入路徑的字符串,能夠方便地調用它們來處理錯誤狀況。

這些值是:

url分發

include包含其餘URLconfs 進行分發

在任什麼時候候,你的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),
    ])),
]

非include包含分發

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 中的每一個視圖都接收你傳遞給它們的額外的選項時纔有價值。

url反向解析

  咱們在html或者view函數中常常能用到url,可是這些url一旦寫死了,若是路徑改了,這些寫死的url全廢了,所有都要改,關鍵還不必定能改全,因此就須要一種方式來動態的獲取url,不去寫死url,這種方式就叫url反向解析

在須要URL 的地方,對於不一樣層級,Django 提供不一樣的工具用於URL 反查:

  • 在模板中:使用url模板標籤。
  • 在Python 代碼中(view):使用django.core.urlresolvers.reverse() 函數。
  • 在更高層的與處理Django 模型實例相關的代碼中:使用get_absolute_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>
  • 在Python (view)代碼中,這樣使用:
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了。

相關文章
相關標籤/搜索