Django之路由系统

Django是如何处理一个请求

官方文档

1、Django确定要使用的根URLconf模块。通常,这是ROOT_URLCONF设置的值,但是如果传入 HttpRequest对象具有urlconf 属性(由中间件设置),则将使用其值代替 ROOT_URLCONF设置。

2、Django加载该Python模块并查找变量 urlpatterns这是django.urls.path() 和/或django.urls.re_path()实例的Python列表

3、Django按顺序运行每个URL模式,并在与请求的URL匹配的第一个停止。

4、一旦其中一个URL模式匹配,Django就会导入并调用给定的视图,该视图是一个简单的Python函数(或基于类的视图)。该视图将传递以下参数:

  • 的实例HttpRequest
  • 如果匹配的URL模式未返回命名组,则来自正则表达式的匹配项将作为位置参数提供。
  • 关键字参数由与路径表达式匹配的任何命名部分组成,并由或 的可选kwargs参数中指定的任何参数覆盖 django.urls.path()django.urls.re_path()

5、如果没有URL模式匹配,或者在此过程中的任何时候引发异常,Django都会调用一个适当的错误处理视图。请参阅下面的错误处理。

例子: 

 1 from django.urls import path
 2 
 3 from . import views
 4 
 5 urlpatterns = [
 6     path('articles/2003/', views.special_case_2003),
 7     path('articles/<int:year>/', views.year_archive),
 8     path('articles/<int:year>/<int:month>/', views.month_archive),
 9     path('articles/<int:year>/<int:month>/<slug:slug>/', views.article_detail),
10 ]

  

  • 捕获的值可以选择包括转换器类型。例如,用于 <int:name>捕获整数参数。如果不包括转换器/,则匹配字符以外的任何字符串
  • 无需添加斜杠,因为每个URL都有该斜杠。例如articles,不是/articles
  • 要从URL捕获值,请使用尖括号。

注意:在使用如<int:year>传递参数的时候,传递的参数一定要被对应的函数接收。

请求示例:

  • 请求/articles/2005/03/匹配列表中的第三个条目。Django将调用该函数 views.month_archive(request, year=2005, month=3)
  • /articles/2003/会匹配列表中的第一个模式,而不是第二个,因为这些模式是按顺序测试的,而第一个是第一个通过的测试。随意利用命令来插入类似这样的特殊情况。在这里,Django将调用该函数 views.special_case_2003(request)
  • /articles/2003 不会与任何这些模式匹配,因为每种模式都要求URL以斜杠结尾。
  • /articles/2003/03/building-a-django-site/将匹配最终模式。Django将调用该函数 views.article_detail(request, year=2003, month=3, slug="building-a-django-site")

路径转换器

默认情况下,以下路径转换器可用:

  • str-匹配任何非空字符串,但路径分隔符除外'/'如果表达式中不包含转换器,则为默认设置。
  • int-匹配零或任何正整数。返回一个int
  • slug-匹配由ASCII字母或数字以及连字符和下划线字符组成的任何条形字符串。例如, building-your-1st-django-site
  • uuid-匹配格式化的UUID。为防止多个URL映射到同一页面,必须包含破折号,并且字母必须小写。例如,075194d3-6885-417e-a8a8-6c931e272f00返回一个 UUID实例。
  • path-匹配任何非空字符串,包括路径分隔符 '/'这样,您就可以与完整的URL路径进行匹配,而不仅仅是与URL路径的一部分进行匹配str

使用正则表达式

如果路径和转换器语法不足以定义URL模式,则还可以使用正则表达式。为此,请使用 re_path()代替path()

在Python正则表达式中,命名正则表达式组的语法为(?P<name>pattern),其中name是组的名称,并且 pattern是匹配的某种模式。

这是前面的示例URLconf,使用正则表达式重写:

1 from django.urls import path, re_path
2 
3 urlpatterns = [
4     path('articles/2003/', views.special_case_2003),
5     re_path(r'^articles/(?P<year>[0-9]{4})/$', views.year_archive),
6     re_path(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/$', views.month_archive),
7     re_path(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/(?P<slug>[w-]+)/$', views.article_detail),
8 ]

 这可以完成与上一个示例大致相同的操作,除了: 

  • 将要匹配的确切URL受到更多限制。例如,年份10000将不再匹配,因为年份整数被限制为正好是四位数长。
  • 无论正则表达式进行哪种匹配,每个捕获的参数都将作为字符串发送到视图。 

当从使用path()切换为使用 re_path()时,特别重要的是要注意视图参数的类型可能会更改,因此您可能需要调整视图。 

嵌套参数

 考虑以下URL模式,这些URL模式可以选择采用page参数:

urls.py中:
1
from django.urls import path, re_path 2 from app01 import views 3 4 urlpatterns = [ 5 path('admin/', admin.site.urls), 6 re_path(r'^blog/(page-(d+)/)?$', views.test), 7 re_path(r'^comments/(?:page-(?P<page_number>d+)/)?$', views.test2), 8 ]
 views.py中:
1
def test(request, arg1, arg2): 2 print(request) 3 print(arg1) 4 print(arg2) 5 return HttpResponse("空页面") 6 7 8 def test2(request, page_number): 9 print(request) 10 print(page_number) 11 return HttpResponse("空页面2")

 例如:

blog/page-2/将导致与匹配blog_articles两个位置参数:page-2/2。在视图函数中要接收这两个位置参数。

comments/page-2/与关键字参数 page_number设置为2 匹配。在视图函数中只需要接收page_number一个参数即可。 

分组命名匹配 

在更高级的用法中,可以使用分组命名匹配的正则表达式组来捕获URL中的值并以关键字参数形式传递给视图。

在Python的正则表达式中,分组命名正则表达式组的语法是(?P<name>pattern),其中name是组的名称,pattern是要匹配的模式。

注意:分组命名匹配中视图函数在接收参数中必须写入分组关键字参数名。

根据参数类型分组匹配:

1 urlpatterns = [
2    path('articles/2003/', views.special_case_2003),
3    path('articles/<int:year>/', views.year_archive),
4    path('articles/<int:year>/<int:month>/', views.month_archive),
5    path('articles/<int:year>/<int:month>/<slug:slug>/', views.article_detail),
6 ]

 正则分组命名匹配:

正则分组命名匹配获取的全是字符串类型

1 urlpatterns = [
2    path('articles/2003/', views.special_case_2003),
3    re_path(r'^articles/(?P<year>[0-9]{4})/$', views.year_archive),
4    re_path(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/$', views.month_archive),
5    re_path(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/(?P<slug>[w-]+)/$', views.article_detail),
6  ]

 include其他的URLconfs

当包含的url太多,或者对url进行分不同功能app分类管理时,就需要用到包含其他URLconfs的方法。

 path('app01/', include(('app01.urls',"app01"), namespace="app01")),
 path('app02/', include(('app02.urls', "app02"), namespace="app02")),

传递额外的参数给视图函数(了解)

path()函数可以使用可选的第三个参数,该参数应该是传递给view函数的额外关键字参数的字典。

urlpatterns = [
    path('blog/<int:year>/', views.year_archive, {'foo': 'bar'}),
]

将额外的选项传递给include()

同样,您可以将额外选项传递给include(),所包含的URLconf中的每一行都将传递额外选项。

例如,这两个URLconf集在功能上是相同的:

设置一:

 1 from django.urls import include, path
 2 
 3 urlpatterns = [
 4     path('blog/', include('inner'), {'blog_id': 3}),
 5 ]
 6 
 7 # inner.py
 8 from django.urls import path
 9 from mysite import views
10 
11 urlpatterns = [
12     path('archive/', views.archive),
13     path('about/', views.about),
14 ]

设置二:

 1 # main.py
 2 from django.urls import include, path
 3 from mysite import views
 4 
 5 urlpatterns = [
 6     path('blog/', include('inner')),
 7 ]
 8 
 9 # inner.py
10 from django.urls import path
11 
12 urlpatterns = [
13     path('archive/', views.archive, {'blog_id': 3}),
14     path('about/', views.about, {'blog_id': 3}),
15 ]

URL的反向解析

Django 提供一个办法是让URL 映射是URL 设计唯一的地方。你填充你的URLconf,然后可以双向使用它:

  • 根据用户/浏览器发起的URL 请求,它调用正确的Django 视图,并从URL 中提取它的参数需要的值。
  • 根据Django 视图的标识和将要传递给它的参数的值,获取与之关联的URL。

第一种方式是我们在前面的章节中一直讨论的用法。第二种方式叫做反向解析URL、反向URL 匹配、反向URL 查询或者简单的URL 反查。
在需要URL 的地方,对于不同层级,Django 提供不同的工具用于URL 反查:

  • 在模板中:使用url模板标签。
  • 在Python 代码中:使用django.core.urlresolvers.reverse() 函数。
  • 在更高层的与处理Django 模型实例相关的代码中:使用get_absolute_url() 方法。

考虑下面的URLconf:

1 from django.conf.urls import url
2 
3 from . import views
4 
5 urlpatterns = [
6     # ...
7     url(r'^articles/([0-9]{4})/$', views.year_archive, name='news-year-archive'),
8     # ...
9 ]

 根据这里的设计,某一年nnnn对应的归档的URL是/articles/nnnn/

你可以在模板的代码中使用下面的方法获得它们: 

<a href="{% url 'news-year-archive' 2012 %}">2012 Archive</a>

<ul>
{% for yearvar in year_list %}
<li><a href="{% url 'news-year-archive' yearvar %}">{{ yearvar }} Archive</a></li>
{% endfor %}
</ul>

 在Python 代码中,这样使用:

1 from django.http import HttpResponseRedirect
2 from django.urls import reverse
3 
4 def redirect_to_year(request):
5     # ...
6     year = 2006
7     # ...
8     return HttpResponseRedirect(reverse('news-year-archive', args=(year,)))

 如果出于某种原因决定更改发布年度文章存档内容的URL,则只需要更改URLconf中的条目即可。

在视图具有一般性质的某些情况下,URL和视图之间可能存在多对一关系。对于这些情况,在反向URL时,视图名称并不是一个足够好的标识符。阅读下一节以了解Django为此提供的解决方案。

命名空间模式

即使不同的APP使用相同的URL名称,URL的命名空间模式也可以让你唯一反转命名的URL。

举个例子:

project中的urls.py

from django.conf.urls import url, include
 
urlpatterns = [
    url(r'^app01/', include('app01.urls', namespace='app01')),
    url(r'^app02/', include('app02.urls', namespace='app02')),
]

app01中的urls.py

from django.conf.urls import url
from app01 import views
 
app_name = 'app01'
urlpatterns = [
    url(r'^(?P<pk>d+)/$', views.detail, name='detail')
]

app02中的urls.py

from django.conf.urls import url
from app02 import views
 
app_name = 'app02'
urlpatterns = [
    url(r'^(?P<pk>d+)/$', views.detail, name='detail')
]

现在,我的两个app中 url名称重复了,我反转URL的时候就可以通过命名空间的名称得到我当前的URL。

语法:

'命名空间名称:URL名称'

模板中使用:

{% url 'app01:detail' pk=12 %}

 views中的函数中使用:

v = reverse('app01:detail', kwargs={'pk':11})

这样即使app中URL的命名相同,我也可以反转得到正确的URL了。

遗留的问题:

自定义路径转换器还没能成功运行。

原文地址:https://www.cnblogs.com/newway644617704/p/13040066.html