国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > Python > 正文

Python探索之URL Dispatcher實例詳解

2020-01-04 16:24:45
字體:
來源:轉載
供稿:網友

URL dispatcher簡單點理解就是根據URL,將請求分發到相應的方法中去處理,它是對URL和View的一個映射,它的實現其實也很簡單,就是一個正則匹配的過程,事先定義好正則表達式和該正則表達式對應的view方法,如果請求的URL符合這個正則表達式,那么就分發這個請求到這個view方法中。

有了這個base,我們先拋出幾個問題,提前思考一下:

這個映射定義在哪里?當映射很多時,如果有效的組織?

URL中的參數怎么獲取,怎么傳給view方法?

如何在view或者是template中反解出URL?

好,先來看一個簡單的例子:

from django.conf.urls import patterns, url, includeurlpatterns = patterns('',  url(r'^articles/2003/$', 'news.views.special_case_2003'),  url(r'^articles/(/d{4})/$', 'news.views.year_archive'),)urlpatterns += patterns('',  url(r'^articles/(?P<year>/d{4})/(?P<month>/d{2})/$', 'news.views.month_archive'),  url(r'model/', include('model_test.urls')),)

這段代碼就是一個URL Dispatcher的例子,它在一個單獨的python/286448.html">python/140649.html">python模塊定義,Django中管這個模塊叫做URLconf,其實,就是通過python代碼方式實現的配置文件,在這個配置中定義了URL路徑和對應的處理方法之間的映射。在Django中,是通過“樹”的結構來管理URLconf之間的關系的,在Django中的主配置文件中,有一個叫做ROOT_URL_CONF的配置項,就是用來指定根URLconf,從根URLconf開始,逐條進行匹配,直到找到匹配項為止,這就是我們上面提到的第一個問題的答案,下面還會再仔細剖析。

在上面例子中,我們可以看到有3個方法:patterns, url, include。url方法構建了一個URL到View方法的映射關系對象,patterns將這些映射關系對象組織成為一個python的列表,那include是做什么的呢?它就是我們上面說到的“樹”結構關系的聯系者,include會關聯其他的URLconf到本URLconf,也就是說include關聯的是孩子節點。整個URL dispatcher體系,就是由這三個方法構建起來的,下面我們重點來介紹這三個方法,了解了這三個方法,整個URL映射機制就會非常清楚了。

def patterns(prefix, *args):  passdef url(regex, view, kwargs=None, name=None, prefix=''):  passdef include(arg, namespace=None, app_name=None):  pass

url()

先來看下最重要的url()方法。第一個參數regex是代表URL的正則表達式,第二個參數指定了和該正則表達式映射的View,此外,還可以通過kwargs參數,給view方法指定默認的kwargs參數,還有name參數,用來命名該URL,主要用在URL反解中,至于prefix用處不大,不解釋。

url()方法最終構造了一個對象,我們姑且叫它URL映射對象,當第一次訪問這個對象去匹配URL時,它會把這個對象中的正則表達式編譯一次,然后保存在該對象中,所以以后再次匹配時,就會很快,不會重復編譯該正則表達式了。在這里正則匹配其實就是用就是python的re模塊,使用過程大致如下:

# 第一次訪問時,編譯,然后保存在url對象中regex = re.compile(regex_str, re.UNICODE)# 每次URL訪問時,進行正則匹配match = regex.search(path)kwargs = match.groupdict()args = match.groups()

注意,這里涉及到了上面提到的第二個問題,即URL中的參數是如何獲取,如何傳遞給view方法的。從URL中獲取參數,其實是通過re模塊中named groups和non-named groups的概念來獲取的,通過match.groupdict()得到的是named groups,其實就是一個字典,字典的key是在URL中指定的,該字典會作為kwargs參數傳遞給view,而通過match.groups()得到的是non-named groups,是一個元組,即tuple,該元組會作為args參數傳遞給view。不過,這里的args和kwargs是不能夠同時存在的,當有kwargs不為空時,args就會被置空,當kwargs為空時,args才會被用到,而傳遞給view的kwargs就只有url()方法中指定的默認kwargs。也就是說,如果你在URL中使用了named groups,那么non-named groups就會被忽略,如果只使用了non-named groups,它才會被作為args參數,傳遞給view方法。

好,糾結了一大堆,還是來解析一下我們上面提到的例子,假如我們有url和view

urls:

url(r'^articles/(/d{4})/$', 'news.views.year_archive')url(r'^articles/(?P<year>/d{4})/(?P<month>/d{2})/$', 'news.views.month_archive')

views:

def year_archive(request, *args, **kwargs):  passdef month_archive(request, *args, **kwargs):  pass

當我們訪問”articles/2014/”這個路徑的時候,解析的過程如下:

>>> import re>>> regex = re.compile(r'^articles/(/d{4})/$', re.UNICODE)>>> match = regex.search("articles/2014/")>>> match.groupdict(){}>>> match.groups()('2014',)

所以最終傳遞給year_archive()方法中的參數應該是這樣的:

(Pdb) pp args(u'2014',)(Pdb) pp kwargs{}

當我們訪問”articles/2014/11”這個路徑時,解析的過程如下:

>>> import re>>> regex = re.compile(r'^articles/(?P<year>/d{4})/(?P<month>/d{2})/$', re.UNICODE)>>> match = regex.search("articles/2014/11/")>>> match.groupdict(){'year': '2014', 'month': '11'}>>> match.groups()('2014', '11')

所以最終傳遞給month_archive()方法中的參數應該是這樣的:

(Pdb) pp args()(Pdb) pp kwargs{'month': u'11', 'year': u'2014'}

再羅嗦一句,因為url()可以指定一個kwargs參數,它是該url關聯的view()方法的默認kwargs參數,也就是說如果在url()方法中指定了kwargs,那么會將這個參數的內容,也傳遞到view方法中的kwargs參數中。

好,至此,url()方法基本上就清楚了,第二個問題也解決了,至于name參數,到下面講到URL反解的時候再詳細解釋。

patterns()

接下來,我們來看patterns()方法,這個其實比較簡單,它就是返回一個由url()方法構造的URL映射對象組成的列表。它有一個必填參數是prefix,這個prefix是它所包含的view的公共前綴,這么做是為了避免代碼重復,比如:

urlpatterns = patterns('',  url(r'^articles/(/d{4})/$', 'news.views.year_archive'),  url(r'^articles/(/d{4})/(/d{2})/$', 'news.views.month_archive'),  url(r'^articles/(/d{4})/(/d{2})/(/d+)/$', 'news.views.article_detail'),)

可以寫成:

urlpatterns = patterns('news.views',  url(r'^articles/(/d{4})/$', 'year_archive'),  url(r'^articles/(/d{4})/(/d{2})/$', 'month_archive'),  url(r'^articles/(/d{4})/(/d{2})/(/d+)/$', 'article_detail'),)

注意,由patterns()生成的列表,被賦值給urlpatterns這個變量,這個變量是不能隨便定義的,必須是約定好的,默認django會去URLconf中查找這個變量,也許你可以在某個地方設定一個參數,來換個約定,改變一下這個變量名。

在2.0版本的Django中,會舍棄這個方法,而是直接賦值給urlpatterns一個列表,不做過多討論。

include()

我們來說include(),這其實是個難點,關鍵在于URL反解那里,Django的文檔也沒有說清楚,而且關系也比較亂,所以,必須得實際的測試一下,才會明白。

上面說過,include()是“樹”結構關系的聯系者,include會關聯其他的URLconf到本URLconf,靠include()才能夠讓Django的URL設計變得非常的靈活和簡潔。include()有三個參數,第一個參數不必多說,它指定了要包含的其它URLconf的路徑,關鍵是剩下的兩個參數,一個是namespace, 一個是app_name,有什么用呢?其實,這兩個參數再加上url()方法中的name參數,共同構成了Django中URL的命名空間,而命名空間主要是為了URL反解的,那什么是URL反解呢?我們現在能根據請求的一個URL路徑,找到對應的view處理方法,那么反過來,我們在view方法中,或者是template中,根據傳遞過來的參數,能夠解析出對應的URL,這就是URL反解。為什么需要URL反解呢?主要是為了不要把程序寫死了,如果我們在html中直接把路徑寫死了,那么以后改起來就會非常的麻煩,所以常常會把這些可變的東西放到一個變量中,在程序中引用的是這個變量名,這是寫程序的一個常識吧。所以,我們能從這個“樹”中,從上到下,也得能夠從下到上。

在template中進行反解使用的是{%url%}這個tag,在view中,進行反解,使用的是`django.core.urlresolvers.reverse()這個方法。

好,先來看一個最簡單的例子:

mydjango/urls.py:

urlpatterns = patterns('',  url(r'model/', include('model_test.urls')),)

model_test/urls.py:

urlpatterns = patterns('',  url(r'^$', views.index, name='index'),)

mydjango/urls.py是根URLconf,它包含了model_test的URLconf,modul_test中的urlpatterns中有一個命名為index的url映射對象。

如果我們想在template中得到這個view對應的url的真實路徑,那么用template的url tag就行了:

{% url 'index' %}

這樣得到的結果就是: /model/。

同理,如果在view方法中,那么使用reverve()方法:

from django.core.urlresolvers import reversereverse("index")

得到的也是: /model/

在這個例子中,我們只使用到了url()方法中的name參數,并沒有用到命名空間,因為這種簡單的情況,沒有產生混淆,還沒有必要用到命名空間,使用命名空間的主要有以下兩種情況:

當在一個項目中,有多個應用,應用中定義的url映射對象的name有可能有重復的,這樣當進行反解時,Django就不能確定到底是哪個應用了

當在一個項目中,同一個應用,被部署多個實例時,這多個實例之間是共享定義的name url的,所以在進行反解時,也不能確定,到底是哪個實例

第一種情況,其實是比較好解決的,在每一個應用的include()中,指定不同的namespace參數就可以了,如:

mydjango/urls.py:

urlpatterns = patterns('',  url(r'model/', include('model_test.urls', namespace='model')),)

這樣,在template中或者是reverse中,在name前需要加上namepace進行反解:

{% 'model:index' %}or reverse("model:index")

這樣就可以準確的反解到model_test這個應用中。

第二種情況,什么叫“一個應用,被部署多個實例”呢?其實就是這種情況:

urlpatterns = patterns('',  url(r'model1/', include('model_test.urls')),  url(r'model2/', include('model_test.urls')),)

不同的路徑下,引用的是相同的應用,同一個應用,被實例化了兩次,這種情況,怎么進行區分呢?我能像第一種情況一樣,在include中指定不同的namespace來解決問題嗎?答案是可行的,但是不推薦。要知道,他們引用的是同一個應用,同一個應用意味著什么,意味著代碼是一樣的,你在同一份代碼中,通過if/else來判斷該反解到哪個namespace中,這個做法是非常不優雅的,嚴重違背了Django的DRY原則。

那Django通過什么辦法來解決這個問題呢?它通過app_name + namespace + current_app的方式來解決。namespace, app_name分別為include()的第二個和第三個參數,app_name指定這個應用的名稱,namespace指定這個應用某個實例的url的命名空間,current_app則是根據請求的路徑,解析出的該url的命名空間,也就是namespace,在進行反解時,動態的將該current_app傳遞給反解的函數中,反解的函數就可以根據這個namespace,來確定應該反解到哪個實例中了。同一個應用的多個實例的app_name應該是相同的,而namespace應該是不同的。可能有點亂了,我們再來舉個例子:

mydjango/urls.py:

urlpatterns = patterns('',  url(r'model1/', include('model_test.urls', namespace='model_1', app_name="app")),  url(r'model2/', include('model_test.urls', namespace='model_2', app_name="app")),)

model_test/urls.py:

urlpatterns = patterns('',  url(r'^$', views.index, name='index'),)

model_test/views.py:

from django.shortcuts import renderfrom django.core.urlresolvers import reversedef index(request):  current_app = request.resolver_match.namespace  print reverse("app:index", current_app=current_app)  return render(request, 'model/index.html', current_app=current_app)

model_test/templates/model/index.html:

{% url 'app:index' %}

在view中,首先獲得當前的namespace,然后通過current_app傳遞給reverse(),reverse就可以知道應該解析到哪個實例中了。同理,在templace中,也需要將current_app傳遞過去。這樣,我們就可以動態的反解URL了:

當我們請求的路徑是/model1/時,current_app就是model_1,再根據app_name,就可以準確的反解出該URL為:/model1/,
如果請求的路徑是/model2/,那么current_app就是model_2,反解的路徑就是/model2/。

雖然有點復雜,但是這種情況用的比較少,google了很久,才把這種情況大概弄清楚,也許理解的有不對的地方,待以后實踐去檢驗,現在關鍵在于理解這種機制思想。

全文完,更多詳細的內容,參見Django官方文檔:https://docs.djangoproject.com/en/1.6/topics/http/urls/

總結

以上就是本文關于Python探索之URL Dispatcher實例詳解的全部內容,希望對大家有所幫助。如有不足之處,歡迎留言指出。感謝朋友們對本站的支持。


注:相關教程知識閱讀請移步到python教程頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 北安市| 怀集县| 德化县| 新安县| 清远市| 武冈市| 阜新| 来宾市| 洛隆县| 奉化市| 鸡东县| 铜鼓县| 应城市| 临朐县| 福建省| 乌兰察布市| 娱乐| 屏东县| 惠水县| 荆州市| 墨江| 大田县| 岑溪市| 平安县| 天门市| 刚察县| 华宁县| 大田县| 石台县| 太湖县| 磴口县| 乐昌市| 溆浦县| 长寿区| 武隆县| 巨鹿县| 泸西县| 巴彦县| 新安县| 那曲县| 武邑县|