1. 程式人生 > >django2.0 path方法

django2.0 path方法

轉自:https://kinegratii.github.io/2017/09/25/django2-url-path/

 

9月23,Django 釋出了2.0a1版本,這是一個 feature freeze 版本,如果沒有什麼意外的話,2.0正式版不會再增加新的功能了。按照以往的規律,預計正式版將在12月釋出。

備註:Django 2.0 於12月2日已經正式釋出。 (連結

2.0無疑是一個里程碑版本,移除了對 Python2.7 的支援,最少需要 3.4 以上,建議使用3.5以上的版本。

 

What’s new in Django2.0 文件中一共列出了三個新的特性:

  • 更簡單的URL路由語法 (Simplified URL routing syntax)
  • admin應用的針對移動裝置的優化改進(Mobile-friendly contrib.admin)
  • 支援SQL開窗表示式(Window expressions)

第一個特性,主要用於動態路由定義上。在Django2.0程式碼實現中,主要的變化是新增了 django.urls.path 函式,它允許使用一種更加簡潔、可讀的路由語法。比如之前的版本的程式碼:

1
url(r'^articles/(?P<year>[0-9]{4})/$', views.year_archive),

在新版本中也可以寫為:

1
path('articles/<int:year>/', views.year_archive),

新語法支援型別轉化,在上述的例子中, year_archive 函式接收到的year引數就變成整數而不是字串。

如果你有接觸過 Flask 框架,就會發現和 Variable-Rules 的語法形式和功能都是相類似的。

一 問題引入

下面是 Django1.X 的一段程式碼:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from django.conf.urls import url

def year_archive(request, year):
    year = int(year) # convert str to int
    # Get articles from database

def detail_view(request, article_id):
    pass

def edit_view(request, article_id):
    pass

def delete_view(request, article_id):
    pass

urlpatterns = [
    url('articles/(?P<year>[0-9]{4})/', year_archive),
    url('article/(?P<article_id>[a-zA-Z0-9]+)/detail/', detail_view),
    url('articles/(?P<article_id>[a-zA-Z0-9]+)/edit/', edit_view),
    url('articles/(?P<article_id>[a-zA-Z0-9]+)/delete/', delete_view),
]

考慮下這樣的兩個問題:

第一個問題,函式 year_archive 中year引數是字串型別的,因此需要先轉化為整數型別的變數值,當然 year=int(year) 不會有諸如如TypeError或者ValueError的異常。那麼有沒有一種方法,在url中,使得這一轉化步驟可以由Django自動完成?

第二個問題,三個路由中 article_id 在業務中表示同一個欄位,使用同樣的正則表示式,但是你需要寫三遍,當之後 article_id 規則改變後,需要同時修改三處程式碼,那麼有沒有一種方法,只需修改一處即可?

在 Django2.0 中,可以使用 path 解決以上的兩個問題。

二 使用示例

這是一個簡單的例子:

1
2
3
4
5
6
7
8
9
10
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>/', views.article_detail),
]

基本規則:

  • 使用尖括號(<>)從url中捕獲值。
  • 捕獲值中可以包含一個轉化器型別(converter type),比如使用 <int:name> 捕獲一個整數變數。若果沒有轉化器,將匹配任何字串,當然也包括了 / 字元。
  • 無需新增前導斜槓。

以下是根據 2.0官方文件 而整理的示例分析表:

請求URL 匹配項 檢視函式呼叫形式
/articles/2005/03/ 第3個 views.month_archive(request, year=2005, month=3)
/articles/2003/ 第1個 views.special_case_2003(request)
/articles/2003 -
/articles/2003/03/building-a-django-site/ 第4個 views.article_detail(request, year=2003, month=3, slug=”building-a-django-site”)

三 path轉化器

文件原文是Path converters,暫且翻譯為轉化器。

Django預設支援以下5個轉化器:

  • str,匹配除了路徑分隔符(/)之外的非空字串,這是預設的形式
  • int,匹配正整數,包含0。
  • slug,匹配字母、數字以及橫槓、下劃線組成的字串。
  • uuid,匹配格式化的uuid,如 075194d3-6885-417e-a8a8-6c931e272f00。
  • path,匹配任何非空字串,包含了路徑分隔符

四 自定義轉化器

4.1 定義

對於一些複雜或者複用的需要,可以定義自己的轉化器。轉化器是一個類或介面,它的要求有三點:

  • regex 類屬性,字串型別

  • to_python(self, value) 方法,value是由類屬性 regex 所匹配到的字串,返回具體的Python變數值,以供Django傳遞到對應的檢視函式中。

  • to_url(self, value) 方法,和 to_python 相反,value是一個具體的Python變數值,返回其字串,通常用於url反向引用。

先看看預設的 IntConverter 和 StringConverter 是怎麼實現的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class IntConverter:
    regex = '[0-9]+'

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

    def to_url(self, value):
        return str(value)


class StringConverter:
    regex = '[^/]+'

    def to_python(self, value):
        return value

    def to_url(self, value):
        return value

第二個例子,是自己實現的4位年份的轉化器。

1
2
3
4
5
6
7
8
class FourDigitYearConverter:
    regex = '[0-9]{4}'

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

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

4.2 註冊

使用register_converter 將其註冊到URL配置中:

1
2
3
4
5
6
7
8
9
10
11
from django.urls import register_converter, path

from . import converters, views

register_converter(converters.FourDigitYearConverter, 'yyyy')

urlpatterns = [
    path('articles/2003/', views.special_case_2003),
    path('articles/<yyyy:year>/', views.year_archive),
    ...
]

五 使用正則表示式

如果上述的paths和converters還是無法滿足需求,也可以使用正則表示式,這時應當使用 django.urls.re_path 函式。

在Python正則表示式中,命名式分組語法為 (?P<name>pattern) ,其中name為名稱, pattern為待匹配的模式。

之前的示例程式碼也可以寫為:

1
2
3
4
5
6
7
8
9
10
from django.urls import path, re_path

from . import views

urlpatterns = [
    path('articles/2003/', views.special_case_2003),
    re_path('articles/(?P<year>[0-9]{4})/', views.year_archive),
    re_path('articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/', views.month_archive),
    re_path('articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/(?P<slug>[^/]+)/', views.article_detail),
]

這段程式碼和之前的程式碼實現了基本的功能,但是還是有一些區別:

  • 這裡的程式碼匹配更加嚴格,比如year=10000在這裡就無法匹配。
  • 傳遞給檢視函式的變數都是字串型別,這點和 url 是一致的。

無命名分組

一般來說,不建議使用這種方式,因為有可能引入歧義,甚至錯誤。

六 Import變動

django.urls.path 可以看成是 django.conf.urls.url 的增強形式。

為了方便,其引用路徑也有所變化,請注意下 urls 包路徑的變更,不再是 conf 的子包了,目前和 views 、conf 一樣,被認為是 Django 的核心元件。

1.X 2.0 備註
- django.urls.path 新增,url的增強版
django.conf.urls.include django.urls.include 路徑變更
django.conf.urls.url django.urls.re_path 異名同功能,url不會立即廢棄

七 程式碼改寫

將“問題引入”一節的程式碼使用新的path函式可以改寫如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
from django.urls import path, register_converter
from django.urls.converters import SlugConverter

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

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

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

register_converter(SlugConverter, 'article_id')
register_converter(FourDigitYearConverter, 'year')

def year_archive(request, year):
    print(type(year)) # <class 'int'>
    # Get articles from database

def detail_view(request, article_id):
    pass

def edit_view(request, article_id):
    pass

def delete_view(request, article_id):
    pass

urlpatterns = [
    path('articles/<year:year>/', year_archive),
    path('article/<article_id:article_id>/detail/', detail_view),
    path('articles/<article_id:article_id>/edit/', edit_view),
    path('articles/<article_id:article_id>/delete/', delete_view),
]

八 總結

第一,目前 路由(url)到檢視(View)的流程可以概括為四個步驟:

  1. url匹配
  2. 正則捕獲
  3. 變數型別轉化
  4. 檢視呼叫

Django2.0 和之前相比多了 變數型別轉化 這一步驟。

第二,新的path語法可以解決一下以下幾個場景:

  • 型別自動轉化
  • 公用正則表示式

問題描述可參考 “問題引入” 一節。