路由匹配

上一篇文章中,介绍了整个路由转发流程。接下来让我们具体分析下从浏览器里输入的地址是怎么匹配到我们的路由系统里的。从已有的urls.py里的代码开始分析,首先是urlpatterns.

urlpatterns

这里有必要说明一下: Django1.7.x及以下版本看到的可能是这样的

urlpatterns = patterns('',
    url(r'^admin/', include(admin.site.urls)),
    url(r'^hello/', views.hello_view, name='hello')
)

Django1.8.x - Django2.0版本的同学看到的可能是这样的

urlpatterns = []
    url(r'^admin/', include(admin.site.urls)),
    url(r'^hello/', views.hello_view, name='hello')
)

Django 2.0 版本的同学,urls.py 有比较大的变化(上面 Django 1.8 的在 2.0 中也可以用,是兼容的)看到的就是我们urls.py里配置的那样了。本篇文章还是主要介绍2.0版本的。

urlpatterns = [
    path('admin/', admin.site.urls),
    path('hello/', views.hello_view, name='hello') 
    ]

列表里每一行url或者path的函数代表着一个url和视图函数的映射。每一个映射中由包含着许多路由语法。让我们继续往下看。

path

path(route, view, kwargs=None, name=None)

Django2.0版本中发布的新特性中允许更简单、更易读的URL路由语法。例如,以前Django发布的这个例子:

url(r'^articles/(?P<year>[0-9]{4})/$', views.year_archive),

现在可写成:

path('articles/<int:year>/', views.year_archive),

新语法支持URL参数的类型强制。在该示例中,视图将接收year关键字参数作为整数而不是字符串。此外,在重写的示例中,匹配的URL稍微受限制。例如,年份10000现在将匹配,因为年份整数不会被限制为恰好四位数,因为它们在正则表达式中。 新版本path不支持正则表达式。如果要使用正则表达式,可以使用旧版本django.conf.urls.url()或者新版本django.urls.re_path(),建议使用新版本。旧版本仍可用只是为了向后兼容,故没有立即废弃。path不支持正则表达式,相对应的,其增加了路径转换器

示例

from django.urls import path

from . import views

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

基本规则:

  • 要从URL捕获值,请使用尖括号。
  • 捕获的值可以选择包括转换器类型。例如,用于 捕获整数参数。如果未包含转换器/,则匹配除字符之外的任何字符串。
  • 没有必要添加前导斜杠,因为每个URL都有。例如,articles而不是/articles。

示例请求:

  • 请求/articles/2005/03/与列表中的第三个条目匹配。Django会调用该函数 。views.month_archive(request, year=2005, month=3)
  • /articles/2003/将匹配列表中的第一个模式,而不是第二个模式,因为模式是按顺序匹配的,第一个模式将首先被匹配。在这里,Django会调用该函数 views.special_case_2003(request)
  • /articles/2003 不匹配任何这些模式,因为每个模式都要求URL以斜杠结尾。然而,默认地,任何不匹配或尾部没有斜杠(/)的申请URL,将被重定向至尾部包含斜杠的相同字眼的URL。 (这是受配置文件setting中APPEND_SLASH项控制的)
  • /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字母或数字组成的任何slug字符串,以及连字符(-)和下划线字符(_)。例如, building-your-1st-django-site。
  • uuid - 匹配格式化的UUID。要防止多个URL映射到同一页面,必须包含短划线并且字母必须为小写。例如,075194d3-6885-417e-a8a8-6c931e272f00。返回一个 UUID实例。
  • path- 匹配任何非空字符串,包括路径分隔符 '/'。这使您可以匹配完整的URL路径,而不仅仅是URL路径的一部分str。

注册自定义路径转换器

对于更复杂的匹配要求,您可以定义自己的路径转换器。 转换器是一个包含以下内容的类:

  • regex 类属性,字符串类型,转换器索要匹配的正则表达式.
  • to_python(self, value) 方法,value是由类属性 regex 所匹配到的字符串,返回具体的Python变量值,以供Django传递到对应的视图函数中.
  • to_url(self, value) 方法,和 to_python 相反,value是一个具体的Python变量值,返回其字符串,通常用于url反向引用。

先看看默认的 IntConverter 和 StringConverter 是怎么实现的:

class IntConverter:
    regex = '[0-9]+'

    def to_python(self, value):
        return int(value)

    def to_url(self, value):
        return str(value)


class StringConverter:
    regex = '[^/]+'

    def to_python(self, value):
        return value

    def to_url(self, value):
        return value

然后,自定义一个4位年份的转化器

class FourDigitYearConverter:
    regex = '[0-9]{4}'

    def to_python(self, value):
        return int(value)

    def to_url(self, value):
        return '%04d' % value

最后使用register_converter()命令在URLconf中注册自定义转换器类 :

from django.urls import register_converter, path
from . import converters, views

register_converter(converters.FourDigitYearConverter, 'yyyy')

urlpatterns = [
    path('articles/2003/', views.special_case_2003),
    path('articles/<yyyy:year>/', views.year_archive),
    ...
]

使用正则表达式

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

命名正则表达式组

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

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

from django.urls import path, re_path
from . import views

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

这段代码和之前的代码实现了基本的功能,但是还是有一些区别:

  • 这里的代码匹配更加严格,比如year=10000在这里就无法匹配。
  • 传递给视图函数的变量都是字符串类型,这点和 url 是一致的。

未命名正则表达式组

除了命名组语法之外,例如(?P<year>[0-9]{4}),您还可以使用较短的未命名组,例如([0-9]{4})。 一般来说,不建议使用这种方式,因为有可能引入歧义,甚至错误。

引用路径变动

1.x 2.0 备注
- django.urls.path 新增,url的增强版
django.conf.urls.include django.urls.include 路径变更
django.conf.urls.url django.urls.re_path 异名同功能,url不会立即废弃

路由分发

实际上每个应用都应该有一个URLs文件来进行url配置,而不是都放在主目录的URLs中。 所以我们应该为每个应用创建相应的urls文件,然后在主目录的urls文件中通过include导入

from django.contrib import admin
from django.urls import path, include
from myapp import urls as myapp_urls

urlpatterns = [
    path('admin/', admin.site.urls),
    path('myapp/', include(myapp.urls))
]

然后在myapp的目录下创建urls.py

from django.urls import path
from myapp import views

urlpatterns = [
    path('hello/', views.hello_view, name='hello'),
]

然后通过 127.0.0.1:8000/myapp/hello/就可以访问我们的hello world界面了

Copyright © itrunner.cn 2020 all right reserved,powered by Gitbook该文章修订时间: 2022-08-28 07:44:16

results matching ""

    No results matching ""