目錄
URLconf是如何工作的?
path和re_path方法
更多URL配置示例
URL的命名及reverse()方法
使用命名URL
硬編碼URL - 不建議
URL指向基于類的視圖(View)
通過URL傳遞額外的參數
小結
Django的項目文件夾和每個應用(app)目錄下都有urls.py
文件,它們構成了Django的路由配置系統(URLconf)。服務器收到用戶請求后,會根據用戶請求的url地址和urls.py里配置的url-視圖映射關系,去調用執行相應的視圖函數或視圖類,最后由視圖返回給客戶端數據。
一個優美的URL不僅層次分明、邏輯清晰,而且便于搜索引擎收錄。一個糟糕的URL不僅可讀性差,而且易造成程序沖突。本章小編我將給大家詳細介紹下如何在Django項目開發中進行路由配置。
URLconf是如何工作的?
假如我們有一個blog
的博客應用,你需要編寫兩個視圖函數,一個用于展示文章列表,一個用于展示文章詳情,你的urls.py
和views.py
正常情況下應如下所示:
# blog/urls.py
from django.urls import path
from . import viewsurlpatterns = [path('blog/', views.index),path('blog/articles/<int:id>/', views.article_detail),
]# blog/views.py
def index(request):# 展示所有文章def article_detail(request, id):# 展示某篇具體文章
那么上面這段代碼是如何工作的呢?
- 當用戶在瀏覽器輸入
/blog/
時,URL收到請求后會調用視圖views.py
里的index
方法,展示所有文章 - 當用戶在瀏覽器輸入
/blog/article/<int:id>/
時,URL不僅調用了views.py
里的article_detail
方法,而且還把參數文章id通過<>
括號的形式傳遞給了視圖。int這里代表只傳遞整數,傳遞的參數名字是id。
在上述代碼中,我們通過urlpatterns
列表的url-視圖映射關系列表起了決定性作用,起到了任務調度的作用。
注意:注意當你配置URL時,別忘了把你的應用(blog)的urls加入到項目的URL配置里(mysite/urls.py), 如下圖所示:
from django.urls import include, pathurlpatterns = [path('', include('blog.urls')),...
]
path和re_path方法
寫個URL很簡單,但如何通過URL把參數傳遞給給視圖view是個技術活。Django提供了兩種設計URL的方法:?path
和re_path
,它們均支持向視圖函數或類傳遞參數。path
是正常參數傳遞,re_path
是采用正則表達式regex匹配。path
和re_path
傳遞參數方式如下:
-
path
方法:采用雙尖括號<變量類型:變量名>
或<變量名>
傳遞,例如<int:id>
,?<slug:slug>
或<username>
。 -
re_path
方法: 采用命名組(?P<變量名>表達式)
的方式傳遞參數。
下例中,我們分別以path
和re_path
?定以了兩個urls,它們是等效的,把文章的id(整數類型)傳遞給了視圖。re_path
里引號前面的小寫r表示引號里為正則表達式,?^
代表開頭,$
代表以結尾,\d+
代表正整數。
# blog/urls.py
from django.urls import path, re_path
from . import viewsurlpatterns = [path('blog/articles/<int:id>/', views.article_detail, name = 'article_detail'),re_path(r'^blog/articles/(?P<id>\d+)/$', views.article_detail, name='article_detail'),
]# blog/views.py
def article_detail(request, id):# 展示某篇文章
在使用path
和re_path
方法設計urls需注意:
- url中的參數名要用尖括號,而不是圓括號;
- 匹配模式的最開頭不需要添加斜杠
/
,但建議以斜杠結尾; - 使用
re_path
時不一定總是以$
結尾,有時不能加。比如下例中把blog.urls
通過re_path
加入到項目urls中時就不能以$
結尾,因為這里的blog/
并不是完整的url,只是一個開頭而已。
from django.urls import include, re_pathurlpatterns = [re_path(r'^blog/', include('blog.urls')),...
]
更多URL配置示例
path
支持匹配的數據類型只有str
,int
,?slug
,?uuid
四種。一般來說re_path
更強大,但寫起來更復雜一些,我們來看看更多案例。
# 示例一,PATH
from django.urls import path
from . import viewsurlpatterns = [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),
]# 示例二:RE_PATH,與上例等同
from django.urls import path, re_path
from . import viewsurlpatterns = [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),
]
同樣以博客為例,如果你希望設計不同的urls分別對應負責增刪改查操作的視圖函數或類,你可以按如下設計:
# blog/urls.py
from django.urls import path, re_path
from . import views# app_name = 'blog' # 命名空間,后面會用到。
urlpatterns = [path('blog/articles/', views.article_list, name = 'article_list'),path('blog/articles/create/', views.article_create, name = 'article_create'),path('blog/articles/<int:id>/', views.article_detail, name = 'article_detail'),path('blog/articles/<int:id>/update/', views.article_update, name = 'article_update'),path('blog/articles/<int:id>/delete/', views.article_update, name = 'article_delete'),
]
URL的命名及reverse()方法
你注意到沒?在上述博客示例中,我們中還給每個URL取了一個名字,比如?article_list
和article_create
。這個名字大有用處,相當于給每個URL取了個全局變量的名字。它可以讓你能夠在Django的任意處,尤其是模板內顯式地引用它。假設你需要在模板中通過鏈接指向一篇具體文章,下面那種方式更好?
使用命名URL
{% for article in articles %}<a href="{% url 'article_detail' article.id %}">{{ article.title }}</a>
{% endfor %}
url
是個模板標簽,其作用是對命名的url進行方向解析,動態生成鏈接。
注意:命名的url里有幾個參數,使用url
模板標簽反向生成動態鏈接時,就需要向它傳遞幾個參數。比如我們的命名urlarticle_detail
里有整數型id
這個參數,我們在模板中還需要傳遞article.id
。
硬編碼URL - 不建議
{% for article in articles %}<a href="blog/articles/{{ article.id }}">{{ article.title }}</a>
{% endfor %}
如果你還沒意識到方法1的好處,那么想想吧,假設老板讓你把全部模板鏈接由blog/articles/id改為blog/article/id, 那種方法更快?更改所有html文件里的鏈接,還是只改URL配置里的一個字母?
那么問題來了。假設不同的app(比如news和blog)里都有article_detail
這個命名URL, 我們怎么避免解析沖突呢? 這時我們只需要在blog/urls.py
加上app_name='blog'
這個命名空間即可,然后在模板中以blog:article_detail
使用即可。
{% for article in articles %}<a href="{% url 'blog:article_detail' article.id %}">{{ article.title }}</a>
{% endfor %}
可惜的是命名的URL一般只在模板里使用,不能直接在視圖里使用。如果我們有了命名的URL,我們如何把它轉化成常規的URL在視圖里使用呢?
Django提供的reverse()
方法很容易實現這點。它在視圖中可以對命名urls進行反向解析,生成動態鏈接。
from django.urls import reverse# output blog/articles/id
reverse('blog:article_detail', args=[id])
URL指向基于類的視圖(View)
目前path
和re_path
都只能指向視圖view里的一個函數或方法,而不能直接指向一個基于類的視圖(Class based view)。Django提供了一個額外as_view()
方法,可以將一個類偽裝成方法。這點在當你使用Django自帶的類視圖或自定義的類視圖時非常重要。
具體使用方式如下:
# blog/urls.py
from django.urls import path, re_path
from . import viewsurlpatterns = [# path('blog/articles/', views.article_list, name = 'article_list'),path('blog/articles/', views.ArticleList.as_view(), name='article_list'),
]# View (in blog/views.py)
from django.views.generic import ListView
from .views import Articleclass ArticleList(ListView):queryset = Article.objects.filter(date__lte=timezone.now()).order_by('date')[:5]context_object_name = 'article_list‘template_name = 'blog/article_list.html'
如果你對基于類的視圖還比較困惑,沒有關系,我們后面會做詳細介紹。
通過URL傳遞額外的參數
在你配置URL時,你還可以通過字典的形式傳遞額外的參數給視圖, 而不用把這個參數寫在鏈接里。如下面案例所示:
# blog/urls.py
from django.urls import path, re_path
from . import viewsurlpatterns = [path('', views.ArticleList.as_view(), name='article_list', {'blog_id': 3}),
]
小結
本章我們講解了如何使用path
和re_path
方法進行url配置,并詳細介紹了什么命名的urls以及如何使用url
模板標簽和?reverse
方法對命名urls進行反向解析。下篇文章中我們將正式介紹視圖的編寫,歡迎閱讀。