Django学习之十一:真正理解Django的路由分发和反解url原理
目錄
- URL Dispatcher
- 簡介
- 模式概念
- 對比URLPattern 與 URLResolver (多態的體現)
- 構建子路由幾種方式
- 反解url算法邏輯
URL Dispatcher
簡介
django的url dispatcher 設計是基于一個url mapper來工作的。
這個url mapper主要用在兩個方向:
通過提供的標識,反解出url
Django provides a solution such that the URL mapper is the only repository of the URL design. You feed it with your URLconf and then it can be used in both directions:
???????? ** Starting with a URL requested by the user/browser, it calls the right Django view providing any arguments it might need with their values as extracted from the URL.
???????? ** Starting with the identification of the corresponding Django view plus the values of arguments that would be passed to it, obtain the associated URL.
模式概念
Django的URL 模式非常的清晰和優雅。一個高質量的web應用就需要一個好的URL模式。
Django的URL 助記點:
- 依照MVC模式,通過url 分發到 對應的 view視圖
- 將 url 和 view視圖都封裝到了URLPattern對象,統稱url對象
- url對象放到urlpattern列表中
- urlpattern列表單獨放在一個module中,我們叫url module。一般命名上都叫urls.py
- 每一個django項目,都有一個唯一的叫root_urlconf的url module.這個ROOT_URLCONF時可以配置的放在項目的settings.py中。指定模塊路徑相對于項目的python path 路徑字符串即可,如'luffyapi.urls'
- 也可以通過中間件對HTTPRequest對象添加一個屬性叫urlconf,賦值指定url module,這樣就會使用HttpRequest.urlconf 作為root_urlconf,針對當前request的生命周期。
- 中間件還是什么時候初始化加載url module
- 按著列表順序,第一個匹配到的就停止匹配了。然后import and call view
- url對象不僅提供通過url匹配拿到view,還提供通過名稱拿到url字符串,這就是所謂的反解析url。反解url主要用在重定向響應或者html模板中。還有就是model object定義一個get_absolute_url()對象方法中。
- url對象名稱,通過url對象實例化參數中指定,re_path(r'test',test_view, name='testurl')'
- 還有一個 URLResolver對象,這個對象是urlpattern對象的容器。且URLResolver對象可以嵌套,也就是URLResolver對象看成URLPattern和URLResolver的容器,容器中放置一個URLResolver對象,就是路由的嵌套,也就是子路由。最頂層有一個URLResolver對象,即頂層容器。
- 無論URLPattern對象還是URLResolver對象,都是通過re_path()或者path()得到的。
- 為了提供效率切不浪費內存空間,每個URLPattern的url正則表達式都是第一次訪問時才會編譯(python中有正則表達式對象,放于內存中)
- 判斷實例化為URLResolver對象還是URLPattern對象,根據re_path()或者path()的第二個參數的類型。如果時list或者tuple則實例化為URLResolver對象。如果是callable就實例化為URLPattern對象。
- 所以利用子路由來減少過多url相同前綴的冗余,時最佳實踐。就在前面也所過了,子路由也是有URLResolver對象。所以要通過re_path等來實例化出一個子路由,就得完成一個子路由的構建過程。子路由構建過程具體看本文下面。
- 現在說回url對象反解獲取url字符串的功能
- 對url對象進行命名, 提供實例化時的name參數
- django-app-namespace, 源碼中叫 app_name
- 由于django項目中,app時可插拔可復用的,所以對同一個app的多次使用,就要通過對其進行區別,所以提出了app instance的概念,通過不同子路由方式來邏輯劃分同一個app的場景下,提出了instance namespace。在源碼中就叫 namesapce
- 通過app_name 和 namespace 都可以作為反解url的一個參數
- 查看from django.urls import reverse 的源碼,理解怎么利用 name/app_name/namespcae反解出url對象的實際url字符串的。
- 反解url還要提供args 或者kwargs 參數。
對比URLPattern 與 URLResolver (多態的體現)
通過對比兩個類的定義:
看到,urlresolver也有resovle解析方法。只不過urlresolver的解析會再去加載子url module模塊中的urlpatterns列表。然后再對列表中的進行循環匹配過程,一直嵌套下去,知道最后的return跳出返回一個ResolverMatch對象。而urlpattern的resolver直接就返回ResovlerMatch對象了。只不過前者會有重新加載獲取子url module模塊來獲取urlpatterns的邏輯。
兩個類都用同名的方法,只是表現出來的的狀態有所不同。這就是面向對象多態在代碼中的體現。提供相同的對外接口,展現出來的狀態過程有所不同,最后返回相同的對象。
構建子路由幾種方式
子路由除了減少路由前綴的冗余,還可以滿足多種url前綴使用同一app的業務場景。
方式一
參照源碼,從最low-level源碼層面的方式,參照實例化出URLResolver對象的源碼
if isinstance(view, (list, tuple)): # 這里的view是re_path或path的第二個參數# For include(...) processing.pattern = Pattern(route, is_endpoint=False)urlconf_module, app_name, namespace = viewreturn URLResolver(pattern,urlconf_module,kwargs,app_name=app_name,namespace=namespace,)從源碼可以看出,如果view參數是一個列表或元組類型,那么將會實例化出URLResolver對象,并且對view參數要有且有三個元素。第一個元素可以是子路由的模塊的python path 也可以直接是 url對象的列表(查看URLResolver.url_patterns源碼可以理解);第二個元素和第三個元素都可以空,也可以都有,但是不能只有namespace單獨有。
方式二
django內置的from django.urls import include 提供生成第一種方式view參數的函數
include源碼:
def include(arg, namespace=None):app_name = Noneif isinstance(arg, tuple):# Callable returning a namespace hint.try:urlconf_module, app_name = argexcept ValueError:if namespace:raise ImproperlyConfigured('Cannot override the namespace for a dynamic module that ''provides a namespace.')raise ImproperlyConfigured('Passing a %d-tuple to include() is not supported. Pass a ''2-tuple containing the list of patterns and app_name, and ''provide the namespace argument to include() instead.' % len(arg))else:# No namespace hint - use manually provided namespace.urlconf_module = argif isinstance(urlconf_module, str):urlconf_module = import_module(urlconf_module)patterns = getattr(urlconf_module, 'urlpatterns', urlconf_module)app_name = getattr(urlconf_module, 'app_name', app_name)if namespace and not app_name:raise 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.',)namespace = namespace or app_name# Make sure the patterns can be iterated through (without this, some# testcases will break).if isinstance(patterns, (list, tuple)):for url_pattern in patterns:pattern = getattr(url_pattern, 'pattern', None)if isinstance(pattern, LocalePrefixPattern):raise ImproperlyConfigured('Using i18n_patterns in an included URLconf is not allowed.')return (urlconf_module, app_name, namespace)可以看到提供app_name 而不提供namespace的話是會拋出異常的。
Notice:關于app_name 與 namespace 存在這樣一個依賴邏輯:
意思就是有namespace必須有app_name.
為什么要有這樣的邏輯?
因為這和反解url 算法邏輯有關。看下面說明有關算法邏輯
inlucde()的參數方式,也有幾種:
反解url算法邏輯
參考官方文檔和from django.urls import reverse 函數的源碼。大致可以這樣理解:
1.1 首先將namespace 作為一個app_name 查找,會yield 返回這個app_name 的所有instance的列表。
1.2 然后django會找尋與app_name名字相同的instance namespace作為用于解析name的對象。。
1.3 如果沒有,django會使用最后部署的instance作為解析name的對象。
1.4 如果列表中一個都沒匹配上app_name,那么django會直接通過instanc namespace去查找。
1.5 最后,如果reverse帶入了current_app 參數指定當前的app ,那么就使用當前的URLResolver來解析name。最后這一點有點不好理解特別是在使用reverse與 url tag 上。
轉載于:https://www.cnblogs.com/ZJiQi/p/10339006.html
總結
以上是生活随笔為你收集整理的Django学习之十一:真正理解Django的路由分发和反解url原理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 校验功能算eo还是ilf_如何区分ILF
- 下一篇: Ajax学习笔记(1)