一、urls硬編碼
在反向解析和命名空間之前我們先來說說URLS硬編碼,用django 開發應用的時候,可以完全是在urls.py 中硬編碼配置地址,在views.py中HttpResponseRedirect()也是硬編碼轉向地址,當然在template 中也是一樣了,這樣帶來一個問題,如果在urls.py 中修改了某個頁面的地址(也就是說更改路由系統中對應的路由分發),那么所有的地方(views.py和template中)都要修改。問題出在硬編碼,緊耦合使得在大量的模板中修改 URLs 成為富有挑戰性的項目。來看下面的模板文件index.html中,我們到的鏈接硬編碼成這樣子:
<li><a href="/goods/index/">url硬編碼</a></li>
如果使用軟編碼之后,無論怎么更改路由系統中的路由分發,只有對應的namespace與name屬性值不變,就不必修改在views.py和template中的url,也就是說
<li><a href="{% url "good:index" %}">url軟編碼</a></li>
在templates中更改為軟編碼之后,其實在templates,index.html文件生成的時候,仍然是
<li><a href="/goods/index/">url軟編碼</a></li>
二、URL的反向解析
在使用Django 項目時,一個常見的需求是獲得URL 的最終形式,以用於嵌入到生成的內容中(視圖中和顯示給用戶的URL等)或者用於處理服務器端的導航(重定向等)。
人們強烈希望不要硬編碼這些URL(費力、不可擴展且容易產生錯誤)或者設計一種與URLconf 毫不相關的專門的URL 生成機制,因為這樣容易導致一定程度上產生過期的URL。
獲取一個URL 最開始想到的信息是處理它視圖的標識(例如名字),查找正確的URL 的其它必要的信息有視圖參數的類型(位置參數、關鍵字參數)和值。
Django 提供一個辦法是讓URL 映射是URL 設計唯一的地方。你填充你的URLconf,然后可以雙向使用它:
1、根據用戶/瀏覽器發起的URL 請求,它調用正確的Django 視圖,並從URL 中提取它的參數需要的值。
2、根據Django 視圖的標識和將要傳遞給它的參數的值,獲取與之關聯的URL。
第一種方式是我們常說的根據地址定位URL。
第二種方式叫做反向解析URL、反向URL 匹配、反向URL 查詢或者簡單的URL 反查。
在需要URL 的地方,對於不同層級,Django 提供不同的工具用於URL 反查:
1、在模板中:使用url 模板標簽。
2、在Python 代碼中:使用 django.core.urlresolvers.reverse() 函數。
3、在更高層的與處理Django 模型實例相關的代碼中:使用 get_absolute_url() 方法。
1、命名空間:
URL 命名空間允許你反查到唯一的命名URL 模式,即使不同的應用使用相同的URL 名稱。第三方應用始終使用帶命名空間的URL 是一個很好的實踐。類似地,它還允許你在一個應用有多個實例部署的情況下反查URL。換句話講,因為一個應用的多個實例共享相同的命名URL,命名空間將提供一種區分這些命名URL 的方法。
一個URL 命名空間有兩個部分,它們都是字符串:
<1>、應用命名空間:
它表示正在部署的應用的名稱。一個應用的每個實例具有相同的應用命名空間。例如,可以預見Django 的管理站點的應用命名空間是' admin '。
<2>、實例命名空間:
它表示應用的一個特定的實例。實例的命名空間在你的全部項目中應該是唯一的。但是,一個實例的命名空間可以和應用的命名空間相同。它用於表示一個應用的默認實例。例如,Django 管理站點實例具有一個默認的實例命名空間'admin'。 URL的命名空間使用':' 操作符指定。例如,管理站點應用的主頁使用' admin:index '。它表示' admin ' 的一個命名空間和' index ' 的一個命名URL.
# include函數的API include(arg, namespace=None, app_name=None) # namespace設置實例命名空間,app_name設置應用命名空間 # 不能只設置app_name,否則會報錯,以下是報錯的源碼 if app_name and not namespace: raise ValueError('Must specify a namespace if specifying app_name.')
一般來說,同一應用下的不同實例應該具有相同的應用命名空間,但是,這並不意味着不同應用可以使用相同的實例命名空間,因為實例命名空間在你所有項目中都是唯一的。
問題: 另外在添加命名空間 namespace時可能會出現以下這個問題:
'Specifying a namespace in include() without providing an app_name ' django.core.exceptions.ImproperlyConfigured: Specifying a namespace in include() without providing an app_name is not supported. Set the app_name attribute in the included module, or pass a 2-tuple containing the list of patterns and app_name instead.
解決方案為:
在對應的app應用的urls.py中添加app_name = '[應用名稱]'如下
from django.conf.urls import url from . import views app_name = 'users' # users為當前應用的名稱 urlpatterns = [ url('^$', views.index), url('^(\d+)/$', views.detail), ]
三、url反向解析實例
在我們的django項目中通常App,目錄結構就可以如下daily_fresh_demo
daily_fresh_demo
|----daily_fresh_demo |----__init__.py |----settings.py |----urls.py |----wsgi.py |----df_cart #對商品購物車管理 |---- migrations # 遷移文件目錄 |---- admin.py |---- apps.py |---- models.py |---- test.py |---- urls.py |---- views.py |---- __init__.py |----df_goods #商品以及后台管理 ... |----df_user #用戶管理 ... |----df_order #訂單管理 ...
|----templates
|index.html
1、路由分發:
daily_fresh_demo/daily_fresh_demo/urls.py
from django.contrib import admin from django.urls import path from django.conf.urls import url, include urlpatterns = [ path('admin/', admin.site.urls), url(r'^goods/', include('df_goods.urls', namespace='df_goods')), # 添加實例命名空間 url(r'^user/', include('df_user.urls', namespace='df_user')), url(r'^cart/', include('df_cart.urls', namespace='df_cart')), url(r'^order/', include('df_order.urls', namespace='df_order')), ]
根據路由分發到各個相應的app中。並添加實例命名空間
2、子路由
daily_fresh_demo/df_goods/urls.py
from django.conf.urls import url from . import views app_name = 'df_goods' # 應用命名空間 urlpatterns = [ url('^index/$', views.index, name="index"), ]
df_goods中的路由,添加應用命名空間。並在url函數中添加name屬性。
3、視圖函數
daily_fresh_demo/df_goods/views.py from django.shortcuts import render, reverse def index(request): print(reverse("df_goods:index")) # 利用reverse函數反向解析url
# 打印結果為/goods/index/
return render(request, 'index.html')
4、靜態文件 index.html
daily_fresh_demo/templates/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>index</title>
</head>
<body>
<a href="/goods/index">硬鏈接</a>
<a href="{% url "df_goods:index" %}">軟鏈接</a>
</body>
</html>
5、結果
靜態文件中index.html的url解析結果如下
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>index</title> </head> <body> <a href="/goods/index">硬鏈接</a> <a href="/goods/index/">軟鏈接</a> # 軟編碼通過解析后得到的結果與硬編碼一致 </body> </html>
總結:這樣一來通過命名空間,無論在templates文件中有多龐大的url地址映射,只要使用url軟編碼,在更改路由系統的時候,都能自動生成。而如果使用硬鏈接硬編碼 ,就只能在views.py和靜態文件中逐個修改url地址,不僅耗費時間,更容易產生錯誤。