Django的URLconf解析

一个干净优雅的URL方案是高质量Web应用程序中的一个重要细节。Django可以让你设计URL,无论你想要什么,没有框架限制。没有.php或没有.cgi要求,当然也没有这些 0,2097,1-1-1928,00废话。

万维网创建者Tim Berners-Lee——Cool URIs don't change

Overview(概述)

你可以非正式地创建一个URLconf(URL配置)的Python模块,来给你的APP设计URL 。这个模块是纯Python代码,并且是URL路径表达式与Python函数(您的视图)之间的映射。这种映射可以尽可能短或只要需要。它可以引用其他映射。而且,由于它是纯粹的Python代码,因此可以动态构建它。

Django如何处理请求

当用户从Django支持的站点请求页面时,系统会根据这个算法确定要执行的Python代码:

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

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

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

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

    HttpRequest实例。

    如果匹配的URL模式没有返回任何命名组,则来自正则表达式的匹配作为位置参数提供。

    关键字参数由路径表达式匹配的任何命名部分组成,并由可选kwargs参数to django.urls.path()或中 指定的任何参数覆盖 django.urls.re_path()。

5、如果没有URL模式匹配,或者在此过程中的任何点发生异常,Django将调用适当的错误处理视图。

例子

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),
]

注意:

1)使用尖括号来获取来自URL的值。

2)捕获的值可以选择包含转换器类型。例如,用于 <int:name>捕获整数参数。如果不包含转换器/,则匹配除字符之外的任何字符串。

3)没有必要添加一个斜杠,因为每个URL都有。例如,它articles不是/articles。

示例请求:

1)请求/articles/2005/03/匹配列表中的第三个条目。Django将会调函数views.month_archive(request, year=2005, month=3)

2)/articles/2003/会匹配列表中的第一个模式,而不是第二个模式,因为模式是按顺序测试的,而第一个模式是第一个要传递的测试。这里,Django会调用函数 views.special_case_2003(request)

3)/articles/2003将 不匹配任何这些模式,因为每种模式都要求URL以斜线结尾。

4)/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。

注册自定义路径转换器

对于更复杂的匹配要求,您可以定义自己的路径转换器。

转换器是一个包含以下内容的类:

1)regex类属性,作为一个字符串。

2)to_python(self,value),它处理匹配的字符串转换成要传递到视图函数的类型。如果它不能转换给定的值,它应该能够捕获ValueError异常

3)to_url(self,value),用于处理将Python类型转换为URL中使用的字符串。

例如:

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:

rom 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),
]

这完成了与前面的例子大致相同的事情,除了:

1)匹配的确切URL会受到一些限制。例如,年份10000将不再匹配,因为年份整数限制为四位长。

2)无论正则表达式匹配什么类型,每个捕获的参数都会以字符串形式发送到视图。

从使用切换path()到 re_path()反之亦然时,注意视图参数的类型可能会更改尤为重要,因此你可能需要调整视图

URLconf搜索的内容

URLconf作为普通的Python字符串来搜索请求的URL。这不包括GET或POST参数,或域名。

例如,在请求中https://www.example.com/myapp/,URLconf将查找myapp/。

在请求中https://www.example.com/myapp/?page=3,URLconf将查找myapp/。

URLconf不查看请求方法。换句话说,所有的请求方法——GET,HEAD,POST 等——将被路由到相同的URL相同的函数

指定视图参数的默认值

一个方便的技巧是为视图的参数指定默认参数。这里有一个URLconf和view的例子:

# URLconf
from django.urls import path

from . import views

urlpatterns = [
    path('blog/', views.page),
    path('blog/page<int:num>/', 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()将使用num捕获的任何值。

包括其他的URLconf

在任何时候,您urlpatterns都可以“包含”其他URLconf模块。这实质上是将一组网址“植根于”其他网址之下

例如,下面是Django网站 本身的URLconf的摘录。它包含许多其他URLconf:

from django.urls import include, path

urlpatterns = [
    # ... snip ...
    path('community/', include('aggregator.urls')),
    path('contact/', include('contact.urls')),
    # ... snip ...
]

每当Django遇到时include(),它会截断与该点匹配的URL的任何部分,并将剩余的字符串发送到包含的URLconf以供进一步处理。

另一种可能性是通过使用path()实例列表来包含额外的URL模式 。例如,考虑这个URLconf:

from django.urls import include, path

from apps.main import views as main_views
from credit import views as credit_views

extra_patterns = [
    path('reports/', credit_views.report),
    path('reports/<int:id>/', credit_views.report),
    path('charge/', credit_views.charge),
]

urlpatterns = [
    path('', main_views.homepage),
    path('help/', include('apps.help.urls')),
    path('credit/', include(extra_patterns)),
]

在这个例子中,/credit/reports/URL将由credit_views.report()Django视图处理 。

这可以用来消除重复使用单个模式前缀的URLconf中的冗余。例如,考虑这个URLconf:

from django.urls import path
from . import views

urlpatterns = [
    path('<page_slug>-<page_id>/history/', views.history),
    path('<page_slug>-<page_id>/edit/', views.edit),
    path('<page_slug>-<page_id>/discuss/', views.discuss),
    path('<page_slug>-<page_id>/permissions/', views.permissions),
]

我们可以通过只声明一次公共路径前缀并对不同的后缀进行分组来改善这一点

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

urlpatterns = [
    path('<page_slug>-<page_id>/', include([
        path('history/', views.history),
        path('edit/', views.edit),
        path('discuss/', views.discuss),
        path('permissions/', views.permissions),
    ])),
]

URL的反向解析

处理Django项目时的一个常见需求是可以获取URL的最终形式,以嵌入生成的内容(视图和属性URL,显示给用户的URL等),或者用于处理服务器上的导航流方(重定向等)

强烈希望避免对这些URL进行硬编码(一种费力,不可扩展且容易出错的策略)。同样危险的是设计临时机制来生成与URLconf描述的设计并行的URL,这可能导致生成随着时间的推移变为陈旧的URL。

换句话说,需要的是一个干燥机制。除了其他优势之外,它还允许进行URL设计的演变,而无需查看所有项目源代码来搜索和替换过时的URL。

我们可以获取URL的主要信息是负责处理它的视图的标识(例如名称)。其他必须参与查找正确URL的信息包括视图参数的类型(位置,关键字)和值。

Django提供了一个解决方案,使得URL映射器是URL设计的唯一存储库。你用你的URLconf提供它,然后它可以在两个方向上使用:

1)从用户/浏览器请求的URL开始,它调用正确的Django视图,提供它可能需要的任何参数以及从URL中提取的值

2)从相应的Django视图的标识以及将传递给它的参数的值开始,获取关联的URL。

第一个是我们在前几节讨论过的用法。第二种是所谓的URL反向解析,反向URL匹配,反向URL查询或者仅仅URL反转。

Django提供了用于执行URL反转的工具,以匹配需要URL的不同图层:

1)在模板中:使用url模板标签。

2)在Python代码中:使用该reverse()函数。

3)在与处理Django模型实例的URL相关的更高级别的代码中:get_absolute_url()方法。

示例:

from django.urls import path

from . import views

urlpatterns = [
    #...
    path('articles/<int:year>/', views.year_archive, name='news-year-archive'),
    #...
]

根据这个设计,对应于年份nnnn的档案的URL 是/articles/<nnnn>/。

你可以使用以下方式在模板代码中获得这些内容:

<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,那么您只需要更改URLconf中的条目。

在某些情况下,视图具有通用性,因此URL和视图之间可能存在多对一的关系。对于这些情况,视图名称在倒转URL时不是一个足够好的标识符。

URL命名空间

即使不同的应用程序使用相同的URL名称,URL名称空间也允许您唯一地反转命名的URL模式。对于第三方应用程序来说,总是使用名称空间的URL是一种很好的做法(正如我们在本教程中所做的那样)。同样,它也允许您在部署应用程序的多个实例时反转URL。换句话说,由于单个应用程序的多个实例将共享命名的URL,因此命名空间提供了将这些命名的URL分开的方法。

正确使用URL命名空间的Django应用程序可以为特定站点多次部署。例如django.contrib.admin有一个 AdminSite类,它允许您轻松 部署多个管理员实例。在后面的示例中,我们将讨论在两个不同位置部署投票应用程序的想法,以便我们可以为两个不同的受众(作者和发布者)提供相同的功能。

一个URL命名空间有两部分,它们都是字符串:

1)应用程序名称空间

这描述了正在部署的应用程序的名称。每个应用程序的每个实例都有相同的应用程序名称空间。例如,Django的管理应用程序具有可预测的应用程序名称空间'admin'。

2)实例名称空间

这标识了应用程序的特定实例。实一的例名称空间在整个项目中应该是唯。但是,实例名称空间可以与应用程序名称空间相同。这用于指定应用程序的默认实例。例如,默认的Django管理实例有一个实例名称空间为'admin'。

命名空间URL使用':'运算符指定。例如,管理应用程序的主索引页面是使用引用的'admin:index'。这表示一个命名空间'admin',和一个命名的URL 'index'。

名称空间也可以嵌套。指定的URL 'sports:polls:index'将查找'index'名称空间中命名的模式,该模式'polls'本身在顶级命名空间中定义'sports'。


翻译:URL dispatcher
















  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值