Django如何处理请求
1. 首先确定要使用的根URLconf模块。通常这是ROOT_URLCONF设置的值,但如果传入HttpRequest对象具有urlconf属性(由中间件设置),则将使用其代替ROOT_URLCONF设置。 2. Django加载Python模块并查找变了urlpatterns。这是Python的django.conf.urls.url()的实例列表。 3. Django按顺序遍历每个URL模式,并在匹配请求的URL的第一个模式停止。 4. 一旦其中一个正则表达式匹配,Django就会导入并调用给定的视图。视图传递一下参数: 一个HttpRequest。 如果匹配的正则表达式未返回任务命名组,则正则表达式中的匹配将作为未知参数提供。 关键字参数由正则表达式匹配的任何命名组组成,由可选kwargs参数中指定的任何参数覆盖django.conf.urls.url()。 5. 如果没有正则表达式匹配,或者在此过程中的任何点期间引发了异常,那么Django将调用适当的错误处理视图。
示例
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), ] 参考: 要从URL捕获值,只需在其周围加上括号即可。 没有必要添加前导斜杠,因为每个URL都有。 'r'在每个正则表达式字符串中是可选的,它会告诉Python不转义保留原始的字符串
捕获的参数是以普通python字符串发送给视图 示例请求: 请求/articles/2005/03/与列表中的第三个条目匹配。Django会调用该函数 。views.month_archive(request, '2005', '03') /articles/2003/将匹配列表中的第一个模式,而不是第二个模式,因为模式是按顺序测试的,在这里,Django会调用该函数views.special_case_2003(request) /articles/2003 不匹配任何这些模式,因为每个模式都要求URL以斜杠结尾。 /articles/2003/03/03/将匹配最终模式。Django会调用该函数。views.article_detail(request, '2003', '03', '03')
命名组
上面的示例通过括号来对未命名的正则表达式来捕获值,并将它们作为位置参数传递给视图。在更高级的用法中,可以使用命名的正则表达式来捕获URL参数并将它们作为关键字参数传递给视图。
在Python正则表达式中,命名正则表达式组的语法是(?p<name>pattern),组名称为'name', 'pattern'是要匹配的模式。
上面示例重写为使用命名分组: 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), ] 差别: 捕获的值作为关键字参数传递而不是位置参数传递给视图函数 例如: 要求/articles/2005/03/调用该函数views.month_archive(request, year='2005', month='03'),而不是views.month_archive(request, '2005', '03') 按此方法URLconf会更明确,但命名组语法难看且过于冗长牺牲了简洁性。
匹配/分组算法
以下是URLconf解析器遵循的算法,关于正则表达式中命名组与非命名组:
1,如果有任何命名参数,它将使用这些参数,忽略非命名参数,并以关键字参数传递给视图。
2,否则,它将所有非命名参数作为位置参数传递。
在这俩种情况下,默认要传递给视图的任何额外关键字参数也会传递,例如:
from django.conf.urls import url from . import views urlpatterns = [ url(r'^blog/(?P<year>[0-9]{4})/$', views.year_archive, {'foo': 'bar'}), ] URLconf有一个钩子,允许您将额外的参数作为Python字典传递给视图函数。
指定视图参数的默认值
一个方便的技巧是为视图的参数指定默认参数。例如:
# 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"): ... 如果第一个模式匹配,该page()函数将使用它的默认参数。如果第二个模式匹配,page()将使用num正则表达式捕获的值。
关于错误处理
当django找不到与请求的url匹配的正则表达式时,或者引发异常时,Django将调用错误处理视图。分别是:
handler400 – django.conf.urls.handler400 handler403 – django.conf.urls.handler403 handler404 – django.conf.urls.handler404 handler500 – django.conf.urls.handler500 覆盖方法: 该handler400由视图my_custom_bad_request_view方法重写 handler400 = 'mysite.views.my_custom_bad_request_view' 该handler403由视图my_custom_permission_denied_view方法重写 handler403 = 'mysite.views.my_custom_permission_denied_view' 该handler404由视图my_custom_page_not_found_view方法重写 handler404 = 'mysite.views.my_custom_page_not_found_view' 该handler500由视图my_custom_error_view方法重写 handler500 = 'mysite.views.my_custom_error_view'
包括其他的URLconf
在任何时候,urlpatterns都可以包含其它的URLconf模块。例如:
from django.conf.urls import include, url urlpatterns = [ # ... snip ... url(r'^community/', include('django_website.aggregator.urls')), url(r'^contact/', include('django_website.contact.urls')), # ... snip ... ]
请注意,此示例中的正则表达式没有$,但包含尾部斜杠。每当Django遇到include()时,它都会根据匹配到的部分,并将剩余的字符串发送到包含的URLconf以进行进一步处理。
另一种可能性是通过使用url()示例列表来包含其他URL模式。例如:
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/URL将由credit_views.report()
Django视图处理。
这可用于重复使用单个模式前缀的URLconf中,例如:
from django.conf.urls import url from . import views urlpatterns = [ url(r'^(?P<page_slug>[w-]+)-(?P<page_id>w+)/history/$', views.history), url(r'^(?P<page_slug>[w-]+)-(?P<page_id>w+)/edit/$', views.edit), url(r'^(?P<page_slug>[w-]+)-(?P<page_id>w+)/discuss/$', views.discuss), url(r'^(?P<page_slug>[w-]+)-(?P<page_id>w+)/permissions/$', views.permissions), ]
我们可以通过仅指定公共路径前缀并对不同的后缀进行分组来改进这一点:
from django.conf.urls import include, url from . import views urlpatterns = [ url(r'^(?P<page_slug>[w-]+)-(?P<page_id>w+)/', include([ url(r'^history/$', views.history), url(r'^edit/$', views.edit), url(r'^discuss/$', views.discuss), url(r'^permissions/$', views.permissions), ])), ]
捕获的参数
包含的URLconf可以从父URLconf接收任何捕获的参数,例如:
# 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), ]
在上面的示例中,捕获的‘username’变量会传递给包含的URLconf。
将额外值传递给include()
当传递额外的参数时,include()包含的URLconf中的每一行都将传递额外的参数。
例如:
# main.py from django.conf.urls import include, url urlpatterns = [ url(r'^blog/', include('inner'), {'blogid': 3}), ] # inner.py from django.conf.urls import url from mysite import views urlpatterns = [ url(r'^archive/$', views.archive), url(r'^about/$', views.about), ]
# main.py from django.conf.urls import include, url from mysite import views urlpatterns = [ url(r'^blog/', include('inner')), ] # inner.py from django.conf.urls import url urlpatterns = [ url(r'^archive/$', views.archive, {'blogid': 3}), url(r'^about/$', views.about, {'blogid': 3}), ]
只有在确定所包含的URLconf中的每个视图都接受您传递的额外选项时,此技术才有用。
URL的反向解析
Django提供了用于执行URL反转的工具,这些工具匹配需要在URL的不同层:
在模板中:使用url模板标记。
在Python代码中:使用该reverse()函数。
在与处理Django模型实例的URL相关的更高级代码中:get_absolute_url()方法。
示例: 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' 2012 %}">2012 Archive</a> {# Or with the year in a template context variable: #} <ul> {% for yearvar in year_list %} <li><a href="{% url 'news-year-archive' yearvar %}">{{ yearvar }} Archive</a></li> {% endfor %} </ul> 在Python视图中: 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,)))
在视图具有通用性的情况下,URL和视图之间可能存在多对一关系。对于这种情况,视图名称不是足够好的标识符。
URL命名空间
URL命名空间分为两部分,两部分都是字符串:
- 应用命名空间:
- 这描述了正在部署的应用程序的名称。单个应用程序的每个实例都具有相同的应用程序命名空间。例如,Django的管理应用程序具有可预测的应用程序命名空间
'admin'
。 - 实例命名空间:
- 这标识了应用程序的特定实例。实例名称空间在整个项目中应该是唯一的。但是,实例名称空间可以与应用程序名称空间相同。这用于指定应用程序的默认实例。例如,默认的Django管理实例的实例名称空间为
'admin'
。
命名空间URL使用':'
运算符指定。例如,使用引用管理应用程序的主索引页面'admin:index'
。这表示名称空间'admin'
和命名URL 'index'
。
命名空间也可以嵌套。命名URL 'sports:polls:index'
将查找命名'index'
空间中命名的模式,该模式'polls'
本身在顶级命名空间中定义'sports'
。
命名空间反转
当给出'polls:index'
要解析的命名空间URL(例如)时,Django将完全限定名称拆分为多个部分,然后尝试以下查找:
-
首先,Django寻找匹配的应用程序命名空间(在本例中
'polls'
)。这将产生该应用程序的实例列表。 -
如果定义了当前应用程序,Django会查找并返回该实例的URL解析程序。可以使用 函数的
current_app
参数指定当前应用程序reverse()
。该
url
模板标签使用当前解决视图在当前应用程序的命名空间RequestContext
。您可以通过在request.current_app
属性上设置当前应用程序来覆盖此默认值。 -
如果没有当前的申请。Django寻找默认的应用程序实例。默认应用程序实例是具有与应用程序命名空间匹配的实例命名空间的实例(在此示例中,是被调用的实例)。
polls
'polls'
-
如果没有默认的应用程序实例,Django将选择最后部署的应用程序实例,无论其实例名称是什么。
如果存在嵌套命名空间,则会对命名空间的每个部分重复这些步骤,直到只有视图名称未解析为止。然后,视图名称将被解析为已找到的命名空间中的URL。
URL命名空间和包含的URLconf
包含的URLconf的应用程序命名空间可以用俩种方式指定。
1, 可以在包含的URLconf模块中设置‘app_name’属性,该属性与urlpatterns处于同一级别。必须将实际模块或对模块的字符串引用传递给include(),而不是urlpatterns它自己的列表。
# 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'), ... ] # urls.py from django.conf.urls import include, url urlpatterns = [ url(r'^polls/', include('polls.urls')), ]
分发的polls.urls将具有应用程序命名空间polls。
2, 还可以创造一个包含嵌入式命名空间数据的对象。如果include()是url()的实例列表,则该对象中包含的URL将添加到全局命名空间:
from django.conf.urls import include, url from . import views polls_patterns = ([ url(r'^$', views.IndexView.as_view(), name='index'), url(r'^(?P<pk>d+)/$', views.DetailView.as_view(), name='detail'), ], 'polls') urlpatterns = [ url(r'^polls/', include(polls_patterns)), ]