1. 程式人生 > >django -- urls.py

django -- urls.py

這個模組是python單嗎,而且是一個將URL路徑和python方法進行對映的程式碼。

這個mapping關係可以根據需求,可長可短。可以涉及到其他的mappings。因為是python程式碼,所以也可以進行動態的修改。

django提供了一種將urls翻譯為其他語言的方式。

如何操作一個request請求

 

當用戶發起一個請求的時候,下面就是django如何通過演算法進行選擇執行哪個python方法。

1: django決定簡要使用個URLconfig模組。一般,這是一個ROOT_URLCONFI配置檔案的一個值。但是如果傳送過來的request請求有一個urlconfig屬性。(通過中間軟體設定),那麼這個值在這裡將會用到。

2:django載入python模組,並且尋找url規則進行匹配。這個url規則將會是一個path或者re_path的集合。

3:django通過各種的url規則,將會在第一個匹配到的規則那裡停止下來。

4:一單匹配了其中一個url規則,django將會倒入並且呼叫對應的view。python的方法。view可以獲取下面的資料。
    1:一個httprequest物件
    2:如果url規則返回的是一個沒有命名的組,那麼匹配到的將會是一個位置引數。
    3:關鍵字引數就是re表示式中的命名的組,
    4:


5:如果沒有url規則匹配。或者說在這個過程中丟擲了異常。django將會丟擲異常。

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:通過尖括號進行捕獲值。
2:捕獲的值可以可選的進行轉換型別。比如上面的將year轉化成為int。如果沒有,除了斜槓之外的所有字串都可以匹配。
3:沒有必要去新增前置斜槓,因為每一個url都有。比如說 articales就不需要 /articals.

根據上面的例子,下面的呼叫。

/articles/2005/03/    匹配第3個      views.month_archive(request, year=2005, month=3).
/articles/2003/              匹配第一個    views.special_case_2003(request)
/articles/2003               不匹配
/articles/2003/03/building-a-django-site/   最後一個    views.article_detail(request, year=2003, month=3, slug="building-a-django-site").

 強轉    int

str    除了斜槓以外的所有
int    0或者其他正整數,不包括負數。
slug   負數以及-_字母

path   匹配所有的非空字元包括/

新增強轉規則。

為了更復雜匹配的需求。你自定義conventer類。標準如下:
1: 一個regex屬性,而且是string。
2:一個to_python(self,value)方法,這個返回將匹配到的string轉化為對應型別的值。這個值將會傳入view方法中。如果不能轉換成為給定的值,那麼將會丟擲異常。
3:一個to_url(sel,value)方法,將會將轉化後的資料轉化成為一個字串,這個將在URL中使用。

class FourDigitYearConverter:
    regex = '[0-9]{4}'

    def to_python(self, value):
        return int(value)

    def to_url(self, value):
        return '%04d' % value

通過方法register_converter進行註冊。

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

通過正則表示式。
如果path和converter語法都不能滿足你定義URL規則,那麼你可以通過正則進行獲取。如果要這樣就需要使用re_path,而不是path.
在python正則表示式中。為了獲取一個命名的組,語法如下(?P<name>pattern)。name就是組的名字,pattern就是規則。

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

這個例子和上面的例子中工作的效果完全一模一樣。除了。
1:具體的URL將要匹配得更嚴格。比如,10000年將不會匹配,因為year整型值被認定為四位數。
2:每一個數據都會被當做整型進行匹配,和匹配的順序沒有關係。

當從使用path到使用gre_path,我們需要關切view引數的資料型別可能會發生變化,所以你有必要去修改你的view。反之亦然。

我們同樣可以使用未命名的引數,雖然這樣跟簡單。但是不建議這麼做。除非明確只有一個數據的時候。而且這種方法更容易導致錯誤。還有命名和未命名的組不能混合使用,因為,兩者混合,未命名的將會被忽略掉。

而且同樣不建議使用巢狀組,但是可以通過(?:pattern)進行避免。 

URLconf搜尋用來幹啥的呢?

這個僅僅用來搜尋url,把url當成string型別來使用。這個不包括get,post引數。或者說是域名。

比如說對於 https://www.example.com/myapp/,URLconf僅僅查詢 myapp/.
同樣
https://www.example.com/myapp/?page=3 ,也僅僅查詢myapp,而忽略引數。

urlconf不會檢視requets的方法,也就是說,不管你怎麼樣,都要經過我這兒。

為view設定預設值。

一個很方便的使用方式就是為view的引數設定哦認知。比如說。

# 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.
    ...

所有的在urlpatterns中的規則都只會在第一次使用連結的時候編譯。也就是匹配,逐一匹配url。

urlpattern物件必須是一個由path或者re_path組成的集合。

使用其他的urlpattern

z在任何時候,你的urlpatterns都可以包含其他urlconfig模組的內容。這個root連線必須是前後關係,不能是子父,只能是父子關係的那種。這是為了滿足真正的需要,但是實際上可以 的。
比如下面的這個例子是Django本身對其他的引用。包含了其他的URLconf模組。

from django.urls import include, path

urlpatterns = [
    # ... snip ...
    path('community/', include('aggregator.urls')),
    path('contact/', include('contact.urls')),
    # ... snip ...
]

無論什麼時候Django遇到了include。它都會擷取掉匹配的字串,然後將剩下的交給include裡面的URLconf。去做更深遠的處理。

另一種可能是通過path包含一個額外的URLpattern。比如說下面的例子。

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

這個例子就是在path的第二個引數include中包含了前面的include。然後再urlpatterns的第一個就是呼叫了其他view裡面的方法。上面的credit/reports將會被傳遞給 credit_views.report方法。

當一個prefix被多次重複的使用的時候,我們就可以通過刪除榮譽的方式進行使用。比如下面的這些。

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

確實有點東西呀。

獲取引數。

一種通過include urlconf的方式進行包含的是可以獲取所有捕獲到的資料的,所以下面的這些列子是合法的。

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

也就是說,main.py中捕獲的username將會傳遞給foo.urls.blog中。

可以通過dict的方式傳遞其他額外的資料。

from django.urls import path
from . import views

urlpatterns = [
    path('blog/<int:year>/', views.year_archive, {'foo': 'bar'}),
]

/blog/2005/        views.year_archive(request, year=2005, foo='bar')

如果獲取到和我們傳入的衝突了,那麼將會使用字典裡面的而不是url中捕獲的。

給include傳遞額外的資料。

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

 需要注意的是通過include包含其他conf,所有的path都將會收到傳遞過去的資料。也不管這些view是否真的接受這些引數。也正是這個原因,你最好確認一下是不是所有的urlconf都接受這個額外的資料。

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

和上面的類似。

path的命名

給自己當前的URL命名,可以讓你在其他地方很明顯的呼叫。尤其是在模版裡面。這個強大的特性可以讓你在一個檔案裡面給所有的全域性URL賦值。

path專講

path是一個方法,返回一個元素。元素型別為 <class 'django.urls.resolvers.URLResolver'>

path的原型path(routeviewkwargs=Nonename=None)
 

from django.urls import include, path

urlpatterns = [
    path('index/', views.index, name='main-view'),
    path('bio/<username>/', views.bio, name='bio'),
    path('articles/<slug:title>/', views.article, name='article-detail'),
    path('articles/<slug:title>/<int:section>/', views.section, name='article-section'),
    path('weblog/', include('blog.urls')),
    ...
]


首先route引數應該是一個string型別或者是一個gettext_lazy()方法,這裡麵包含了URL匹配規則。
這個string裡面可能會有尖括號,會將捕獲到的資料傳遞到view中。而且尖括號中可以有型別轉換。這樣也就改變了傳送過去的型別。

view引數一個view的方法或者是as_view,也可以是一個 django.urls.include()方法。

kwargs 引數可以傳遞任意的引數過去。必須是dict型別。

name  普通是順序匹配,這樣可以自由匹配。而且過載的時候更加有效。link

url()是re_path的一個別名。