Handling HTTP requests 之URL dispatcher

综述

设计一个web应用的URLs,你需要新建一个Python模块来进行URL设置(URLconf)。这个模块中纯Python代码的并且是一个URL patterns(正则表达式)与Python函数(views)之间的映射匹配。

Django完成一个网络请求的过程。

<li>通常在setting.py文件中使用ROOT_URLCONF设置app的根URL,访问的HttpRequest对象有一个属性是urlconf,对应的值就是ROOT_URLCONF设置的值.
<li>Django加载上文中新建的模块,并且寻找相应urlpatterns。urlpatterns是一个包含django.conf.urls.url()实例的列表。
<li>Django从上到下搜索每一个URL pattern,在第一个能够匹配的url()实例出停止。
<li>一旦匹配成功Django就会倒入并且调用一个函数(或者是一个class-based view)返回一个视图。获取试图的参数通过以下方式传递
<ol>
<li>通过HttpRequest实例传递。
<li>url()实例的正则表达式中传递参数
<li>django.conf.urls.url() 关键字传递参数
</ol>
<li>如果没有匹配或者匹配过程中出现异常,Django将抛出异常

<pre>
from django.conf.urls import url
from . import views
urlpatterns = [
url(r'^articles/2003/$', views.special_case_2003),
url(r'^articles/([0-9]{4})/$', views.year_archive),
url(r'^articles/([0-9]{4})/([0-9]{2})/$', views.month_archive),
url(r'^articles/([0-9]{4})/([0-9]{2})/([0-9]+)/$', views.article_detail),
]
</pre>
<li>一个http:www.XXX/articles/2005/03/请求将会匹配第三个入口,Django将会调用views.month_archive(request, '2005', '03')函数
<li>/articles/2005/3/不会任何匹配,因为第三个入口中需要月份参数是两位数。
<li>/articles/2003/将会匹配第一个模式而不是第二个。因为第一个匹配成功了,就不会继续往下走
<li>/articles/2003不会有任何的匹配。上文中的每个pattern的结尾都必须要有一个"/"
<li>/articles/2003/03/03/ 匹配最后一个模式。并且调用views.article_detail(request, '2003', '03', '03')函数。

采用 name groups

采用正则表达式(?P<name>pattern)patter来匹配,捕获的之作为关键之参数传递给视图表达式。
<pre>
from django.conf.urls import url
from . import views
urlpatterns = [
url(r'^articles/2003/$', views.special_case_2003),
url(r'^articles/(?P<year>[0-9]{4})/$', views.year_archive),
url(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/$', views.month_archive),
url(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/(?P<day>[0-9]{2})/$', views.article_detail),
]
</pre>

<li>/articles/2005/03/,将会调用 views.month_archive(request, year='2005', month='03')
<li>/articles/2003/03/03/ --->views.article_detail(request, year='2003', month='03', day='03')
同时传递给视图函数的总是strings

URLconf结果

URLconf把一个requested URL当成一个普通的Python string搜索。与请求的方法无关(POST、GET)
https://www.example.com/myapp/ --->匹配 myapp/
https://www.example.com/myapp/?page=3 ----->匹配 myapp/ 参数可以通过视图函数的request对象实例相关方法回去 request.GET['page']

指定默认值
<pre>

URLconf

from django.conf.urls import url
from . import views
urlpatterns = [
url(r'^blog/$', views.page),
url(r'^blog/page(?P<num>[0-9]+)/$', views.page),
]

View (in blog/views.py)

def page(request, num="1"):
# Output the appropriate page of blog entries, according to num.
...

在上面的例子中,两个URL模式指向相同的视图-views.page-。但是第一个模式并没有从URL中捕捉到任何东西。如果第一个模式匹配,那么page()函数将使用它的默认参数num“1”。如果第二个模式匹配,page()将使用regex捕获的任何num值
</pre>

Including other URLconfs

include() 函数,可以把urlpatterns放到别的地方去设置
<pre>
from django.conf.urls import include, url
urlpatterns = [
url(r'^community/', include('django_website.aggregator.urls')),
url(r'^contact/', include('django_website.contact.urls')),
]

把所有以community开头的请求都交由django_website.aggregator.urls去处理
</pre>

也是一种拆分urlpatterns臃肿的办法
<pre>
from django.conf.urls import include, url
from apps.main import views as main_views
from credit import views as credit_views
extra_patterns = [
url(r'^reports/$', credit_views.report),
url(r'^reports/(?P<id>[0-9]+)/$', credit_views.report),
url(r'^charge/$', credit_views.charge),
]
urlpatterns = [
url(r'^$', main_views.homepage),
url(r'^help/', include('apps.help.urls')),
url(r'^credit/', include(extra_patterns)),
]

例子中/credit/reports/ 将有credit_views.report()处理。 现在 url(r'^credit/', include(extra_patterns))匹配,再在extra_patterns 寻找进一步的匹配
</pre>
include()参数的获取
<pre>

In settings/urls/main.py

from django.conf.urls import include, url
urlpatterns = [
url(r'^(?P<username>\w+)/blog/', include('foo.urls.blog')),
]

In foo/urls/blog.py

from django.conf.urls import url
from . import views
urlpatterns = [
url(r'^$', views.blog.index),
url(r'^archive/$', views.blog.archive),
]

diyinqianchang/blog/archive/ --->views.blog.archive(request,username='diyinqianchang')

def archive(request,username=None)
</pre>

Reverse resolution of URLs

在编写web程序时,有时候会需要在网页上嵌入一个连接,用于跳转。也可以用设计好的pattern来产生匹配格式的网址。
<li>在templates中使用此url的标记
<li>在Python代码中reverse()函数 reverse(viewname, urlconf=None, args=None, kwargs=None, current_app=None)
<li> get_absolute_url()函数

name 标记
<pre>
from django.conf.urls import url
from . import views
urlpatterns = [
url(r'^articles/([0-9]{4})/$', views.year_archive, name='news-year-archive'),
]
<a href="{% url 'news-year-archive' 2015%}"></a>
</pre>

reverse()
<pre>
from django.urls import reverse
from django.http import HttpResponseRedirect
def redirect_to_year(request):
year = 2006
return HttpResponseRedirect(reverse('news-year-archive', args=(year,)))
</pre>

URL命名空间

URL 命名空间允许你反查到唯一的,即使不同的应用使用相同的URL 名称。第三方应用始终使用带命名空间的URL 是一个很好的实践。

当解析一个带命名空间的URL(例如'polls:index')时,Django 将切分名称为多个部分,然后按下面的步骤查找:
<li>首先,Django 查找匹配的 application namespace, 在下面的例子中为'polls'。这将得到该应用实例的一个列表。
<li>如果当前应用属性被定义,Django将查找并返回那个实例的URL解析器。当前应用属性可在reverse() 函数中通过current_app来指定。
<li>如果没有当前应用。Django 将查找一个默认的应用实例。默认的应用实例是[instance namespace和application namespace 一致的那个实例(在下面例子中,polls的一个叫做'polls' 的实例)
<li>如果没有默认的应用实例,Django 将挑选该应用最后部署的实例,不管实例的名称是什么。
<li>如果提供的命名空间与第1步中的application namespace 不匹配,Django 将尝试直接将此命名空间作为一个 instance namespace查找。

<pre>urls.py
from django.conf.urls import include, url
urlpatterns = [
url(r'^author-polls/', include('polls.urls', namespace='author-polls')),
url(r'^publisher-polls/', include('polls.urls', namespace='publisher-polls')),
]
</pre>

<pre>polls/urls.py
from django.conf.urls import url
from . import views
app_name = 'polls'
urlpatterns = [
url(r'^$', views.IndexView.as_view(), name='index'),
url(r'^(?P<pk>\d+)/$', views.DetailView.as_view(), name='detail'),
]
</pre>

本例中author-polls和publisher-polls都有相同连写的主页和详细链接,被include() 在polls/urls.py

<li>如果其中一个实例是当前实例 —— 如果我们正在渲染'author-polls' 实例的detail 页面 —— 'polls:index' 将解析成'author-polls' 实例的主页面;例如下面两个都将解析成"/author-polls/"。

<pre>第一和第二
reverse('polls:index', current_app=self.request.resolver_match.namespace)

{% url 'polls:index' %}
</pre>

<li>如果没有当前实例——假如说我们在站点的其他地方渲染页面——'polls:index'将解析到最后注册到polls的实例。因为没有默认的实例(实例命名空间为polls),将用注册的polls的最后一个实例。它将是'publisher-polls',因为它是urlpatterns中最后一个声明的.

<li>'author-polls:index' 将永远解析到 'author-polls' 实例的主页('publisher-polls' 类似) 没有用什么命名空间,用name来标记

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容