Django的URL是如何工作的
URL通常與視圖(View)一起工作的。服務器收到用戶請求後,會根據urls.py裏的關係條目,去視圖View裏查找到與請求對應的處理方法,從而返回給客戶端http頁面數據。這和其它web開發的路由機制(Router)是一個道理。如果你還不知道視圖是什麼,那麼你只需要記住:視圖收到用戶的請求後,展示給用戶看得見的東西。
我們來看看下面一個新聞博客的例子:
# blog/urls.py
from django.urls import path
from . import views
urlpatterns = [
path('blog/', views.index),
path('blog/article/<int:id>/', views.article),
]
# blog/views.py
def index(request):
# 展示所有文章
def article(request, id):
# 展示某篇具體文章
那麼這段代碼是如何工作的?
當用戶在瀏覽器輸入/blog/時,URL收到請求後會調用視圖views.py裏的index方法,展示所有文章。
當用戶在瀏覽器輸入/blog/article/<int:id>/時,URL不僅調用了views.py裏的article方法,而且還把參數文章id通過<>括號的形式傳遞給了視圖。int這裏代表只傳遞整數,傳遞的參數名字是id。
注意當你配置URL時,別忘了把你的app(比如blog)urls加入項目的URL配置裏(mysite/urls.py), 如下圖所示:
from django.conf.urls import url, include
urlpatterns = [
url(r'^/', include('blog.urls')),
]
Django URL傳遞參數的方法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<變量名>表達式)的方式傳遞參數。
下圖兩種傳遞文章id給視圖函數的方式是一樣的。re_path裏引號前面的小寫r表示引號裏爲正則表達式, 請忽略'\'不要轉義,^代表開頭,$代表以結尾,\d+代表正整數。
# blog/urls.py
from django.urls import path, re_path
from . import views
urlpatterns = [
path('blog/article/<int:id>/', views.article, name = 'article'),
re_path(r'^blog/article/(?P<id>\d+)/$', views.article, name='article'),
]
# View (in blog/views.py)
def article(request, id):
# 展示某篇文章
URL的命名及reverse()方法
你注意到沒?我們在上述代碼中還給URL取了一個名字 'article'。這個名字大有用處,相當於給URL取了個全局變量的名字。它可以讓你能夠在Django的任意處,尤其是模板內顯式地引用它。假設你需要在模板中通過鏈接指向一篇具體文章,下面那種方式更好?
方法1: 使用命名URL
<a href="{% url 'article' id %}">Article</a>
方法2: 使用常規URL - 不建議
<a href="blog/article/id">Article</a>
如果你還沒意識到方法1的好處,那麼想想吧,假設你需要把全部模板鏈接由blog/article/id改爲blog/articles/id, 那種方法更快?改所有模板,還是改URL配置裏的一個字母?
可惜的是命名的URL一般只在模板裏使用,不能直接在視圖裏使用。如果我們有了命名的URL,我們如何把它轉化成常規的URL在視圖裏使用呢?Django提供的reverse()方法很容易實現這點。假設不同的app(比如news和blog)裏都有article這個命名URL, 我們怎麼區分呢? 我們只需要在article前面加上blog這個命名空間即可。
from django.urls import reverse
# output blog/article/id
reverse('blog:article', args=[id])
URL如何指向基於類的視圖(View)
目前path和re_path都只能指向視圖view裏的一個函數或方法,而不能指向一個基於類的視圖(Class based view)。Django提供了一個額外as_view()方法,可以將一個類僞裝成方法。這點在當你使用Django在帶的view類或自定義的類時候非常重要。具體使用方式如下:
# blog/urls.py
from django.urls import path, re_path
from . import views
urlpatterns = [
path('', views.ArticleList.as_view(), name='article_list'),
path('blog/article/<int:id>/', views.article, name = 'article'),
re_path(r'^blog/article/(?P<id>\d+)/$', views.article, name='article'),
]
# View (in blog/views.py)
from django.views.generic import ListView
from .views import Article
class ArticleList(ListView):
queryset = Article.objects.filter(date__lte=timezone.now()).order_by('date')[:5]
context_object_name = 'latest_article_list'
template_name = 'blog/article_list.html'
def article(request, id):
# 展示某篇文章
通過URL方法傳遞額外的參數
在你配置URL時,你還可以通過字典的形式傳遞額外的參數給視圖, 而不用把這個參數寫在鏈接裏。如下面案例所示:
# blog/urls.py
from django.urls import path, re_path
from . import views
urlpatterns = [
path('', views.ArticleList.as_view(), name='article_list', {'blog_id': 3}),
re_path(r'^blog/article/(?P<id>\d+)/$', views.article, name='article'),
]