Python之Django之调度器URL

Django如何处理一个请求

当一个用户请求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匹配的第一个模式停下来。
  4. 一旦其中一个URL模式匹配,Django就会导入并调用给定的视图,这是一个简单的Python函数(或基于类的视图)。视图传递以下参数:
    • 一个HttpRequest实例。
    • 如果匹配的URL模式未返回任何命名组,则正则表达式中的匹配将作为位置参数提供。
    • 关键字参数由路径表达式匹配的任何命名部分组成,由或者 可选kwargs参数中指定的任何参数覆盖 。django.urls.path()django.urls.re_path()
  5. 如果没有URL模式匹配,或者在此过程中的任何点期间引发异常,Django将调用适当的错误处理视图。请参阅下面的错误处理

例如

下面是一个简单的URLconf:

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捕获值,请使用尖括号。
  • 捕获的值可以选择包括转换器类型。例如,用于 <int:name>捕获整数参数。如果未包含转换器/,则匹配除字符之外的任何字符串。
  • 没有必要添加前导斜杠,因为每个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以斜杠结尾。
  • /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

注册自定义路径转换器

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

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

  • 一个regexclass属性,作为字符串。
  • 甲方法,它处理匹配的字符串转换成要传递到视图函数的类型。如果它不能转换给定值,它应该提高。to_python(self,value)ValueError
  • 一种方法,用于处理将Python类型转换为要在URL中使用的字符串。to_url(self, value)

例如:

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 path, register_converter

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

这完成了与前一个示例大致相同的事情,除了:

  • 匹配的确切网址稍微受限制。例如,年份10000将不再匹配,因为年份整数被限制为恰好四位数。
  • 无论正则表达式的匹配类型如何,每个捕获的参数都将作为字符串发送到视图。

从使用切换path()到 re_path()反之亦然,特别重要的是要注意视图参数的类型可能会发生变化,因此您可能需要调整视图。

使用未命名的正则表达式组

除了命名组语法之外,例如(?P<year>[0-9]{4}),您还可以使用较短的未命名组,例如([0-9]{4})

不特别推荐这种用法,因为它更容易在匹配的预期含义和视图的参数之间意外引入错误。

在任何一种情况下,建议在给定的正则表达式中仅使用一种样式。当两种样式混合使用时,将忽略任何未命名的组,并且只将命名组传递给视图函数。

嵌套参数

正则表达式允许嵌套参数,Django将解析它们并将它们传递给视图。反转时,Django将尝试填充所有外部捕获的参数,忽略任何嵌套捕获的参数。考虑以下URL模式,它们可选地采用页面参数:

from django.urls import re_path

urlpatterns = [
    re_path(r'^blog/(page-(\d+)/)?$', blog_articles),                  # bad
    re_path(r'^comments/(?:page-(?P<page_number>\d+)/)?$', comments),  # good
]

两种模式都使用嵌套参数并将解析:例如, blog/page-2/将导致blog_articles与两个位置参数匹配:page-2/2。第二个模式comments将匹配comments/page-2/关键字参数 page_number设置为2.在这种情况下,外部参数是非捕获参数(?:...)

blog_articles视图需要扭转最外捕获的参数, page-2/或者在这种情况下没有参数,而comments可与任何参数或值被反转page_number

嵌套捕获的参数在视图参数和URL之间创建强耦合,如下所示blog_articles:视图接收URL(page-2/)的一部分而不是视图感兴趣的值。这种耦合在反转时更加明显,因为反转我们需要传递一段URL而不是页码的视图。

根据经验,只捕获视图需要使用的值,并在正则表达式需要参数但视图忽略它时使用非捕获参数。

URLconf在什么上查找

请求的URL被看到是一个普通的Python字符串,URLconf在其上查找并匹配。进行匹配时将不包括GET或POST请求方式的参数以及域名。

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

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

URL配置不检查使用了哪种请求方法换句话讲,所有的请求方法-即,对同一个URL的无论是POST请求, ,GET请求HEAD请求方法等等-都将路由到相同的函数。

视图指定参数的默认值

有一个方便的小技巧是指定视图参数的默认值。下面是一个URLconf和视图的示例:

# 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()函数将使用它的默认参数num1。如果第二个模式匹配, page()将使用num捕获的任何值。

性能

a中的每个正则表达式在urlpatterns第一次访问时编译。这使得系统非常快。

urlpatterns的变量语法

urlpatterns应该是Python列表path()和/或 re_path()实例。

处理错误

当Django找不到所请求URL的匹配项时,或者引发异常时,Django会调用错误处理视图。

这些情况发生时使用的视图通过4个变量指定。它们的默认值应该满足大部分项目,但是通过赋值给它们以进一步的自定义也是可以的。

的完整细节请参见自定义错误视图 。

这些值得在你的根URLconf中设置。在其它URLconf中设置这些变量将不会生效果。

它们的值必须是可调用的或者是表示视图的Python完整导入路径的字符串,可以方便地调用它们来处理错误情况。

这些值是:

包含其它的的URLconf 

在任何时候,你的urlpatterns都可以“包括”其它URLconf模块。这实际上将一部分URL放置于其它URL下面。

例如,下面是URLconf Django website自己的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),
    ])),
]

的捕获参数

被包含的URLconf会收到来自父URLconf捕获的任何参数,所以下面的例子是合法的:

# In settings/urls/main.py
from django.urls import include, path

urlpatterns = [
    path('<username>/blog/', include('foo.urls.blog')),
]

# In foo/urls/blog.py
from django.urls import path
from . import views

urlpatterns = [
    path('', views.blog.index),
    path('archive/', views.blog.archive),
]

在上面的例子中,的捕获"username"变量将被如期传递给包括()指向的URL配置。

传递额外选项来查看功能

URLconfs有一个钩子,允许您将额外的参数作为Python字典传递给视图函数。

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

例如:

from django.urls import path
from . import views

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

在这个例子中,对于请求/blog/2005/,Django将调用 。views.year_archive(request, year=2005, foo='bar')

联合框架中使用此技术 将元数据和选项传递给视图。

处理冲突

可以使用URL模式捕获命名关键字参数,并在其额外参数字典中传递具有相同名称的参数。发生这种情况时,将使用字典中的参数而不是URL中捕获的参数。

将额外选项传递给include()

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

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

设置一个:

# main.py
from django.urls import include, path

urlpatterns = [
    path('blog/', include('inner'), {'blog_id': 3}),
]

# inner.py
from django.urls import path
from mysite import views

urlpatterns = [
    path('archive/', views.archive),
    path('about/', views.about),
]

设置二:

# main.py
from django.urls import include, path
from mysite import views

urlpatterns = [
    path('blog/', include('inner')),
]

# inner.py
from django.urls import path

urlpatterns = [
    path('archive/', views.archive, {'blog_id': 3}),
    path('about/', views.about, {'blog_id': 3}),
]

请注意,无论行的视图是否实际接受这些选项为有效,额外选项将始终传递到包含的URLconf中的每一行。因此,只有在您确定所包含的URLconf中的每个视图都接受您传递的额外选项时,此技术才有用。

URL的反向解析

在处理Django项目时,通常需要获得最终形式的URL,以嵌入生成的内容(视图和资产URL,向用户显示的URL等)或处理服务器上的导航流。方(重定向等)

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

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

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

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

  • 从用户/浏览器请求的URL开始,它调用正确的Django视图,提供它可能需要的任何参数以及从URL中提取的值。
  • 从标识相应的Django视图以及将传递给它的参数值开始,获取关联的URL。

第一个是我们在前面几节中讨论过的用法。第二个是所谓的URL反向解析反向URL匹配反向URL查找或简单的URL反转

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

  • 在模板中:使用url模板标记。
  • 在Python代码中:使用该reverse()函数。
  • 在与处理Django模型实例的URL相关的更高级代码中:该get_absolute_url()方法。

示例

再次考虑这个URLconf条目:

from django.urls import path

from . import views

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

根据这种设计,对应于年度归档文件的URL NNNN 是/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.http import HttpResponseRedirect
from django.urls import reverse

def redirect_to_year(request):
    # ...
    year = 2006
    # ...
    return HttpResponseRedirect(reverse('news-year-archive', args=(year,)))

如果由于某种原因决定应该更改发布年度文章档案的内容的URL,那么您只需要更改URLconf中的条目。

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

命名URL模式

要执行URL反转,您需要使用命名URL模式, 如上面的示例所示。用于URL名称的字符串可以包含您喜欢的任何字符。您不限于有效的Python名称。

命名URL模式时,请选择不太可能与其他应用程序选择的名称冲突的名称。如果您调用URL模式comment 而另一个应用程序执行相同的操作,则reverse()找到的URL 取决于项目urlpatterns列表中最后一个模式。

在URL名称上添加前缀(可能是从应用程序名称派生的(例如myapp-comment代替comment))可以减少冲突的可能性。

如果要覆盖视图,可以故意选择其他应用程序相同的URL名称。例如,一个常见的用例是覆盖 LoginView。Django和大多数第三方应用程序的部分假定此视图具有名称的URL模式 login。如果你有一个自定义登录查看,并给它的URL名称login, reverse()将只要它在找到你的自定义视图 urlpatterns之后,django.contrib.auth.urls包括(如果这是包含在所有)。

如果参数不同,您也可以对多个URL模式使用相同的名称。除URL名称外,还reverse() 匹配参数的数量和关键字参数的名称。

URL命名空间

介绍

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

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

URL命名空间分为两部分,两部分都是字符串:

应用命名空间

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

实例命名空间

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

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

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

反转命名空间的URL¶

当给出'polls:index'要解析的命名空间URL(例如)时,Django将完全限定名称拆分为多个部分,然后尝试以下查找:

  1. 首先,Django寻找匹配的应用程序命名空间(在本例中'polls')。这将产生该应用程序的实例列表。

  2. 如果定义了当前应用程序,Django会查找并返回该实例的URL解析程序。可以使用 函数的current_app参数指定当前应用程序reverse()

    url模板标签使用当前解决视图在当前应用程序的命名空间 RequestContext。您可以通过在request.current_app属性上设置当前应用程序来覆盖此默认值。

  3. 如果没有当前的应用程序,Django会查找默认的应用程序实例。默认应用程序实例是具有与应用程序命名空间匹配的实例命名空间实例(在此示例中,是被调用的实例)。polls'polls'

  4. 如果没有默认的应用程序实例,Django将选择最后部署的应用程序实例,无论其实例名称是什么。

  5. 如果提供的命名空间与步骤1 中的应用程序命名空间不匹配,Django将尝试直接查找命名空间作为 实例命名空间

如果存在嵌套命名空间,则会对命名空间的每个部分重复这些步骤,直到只有视图名称未解析为止。然后,视图名称将被解析为已找到的命名空间中的URL。

例如

要显示此解决方案策略,请考虑polls本教程中应用程序的两个实例的示例:一个调用'author-polls' ,一个调用'publisher-polls'。假设我们已经增强了该应用程序,以便在创建和显示轮询时考虑实例名称空间。

的urls.py 

from django.urls import include, path

urlpatterns = [
    path('author-polls/', include('polls.urls', namespace='author-polls')),
    path('publisher-polls/', include('polls.urls', namespace='publisher-polls')),
]

民调/的urls.py 

from django.urls import path

from . import views

app_name = 'polls'
urlpatterns = [
    path('', views.IndexView.as_view(), name='index'),
    path('<int:pk>/', views.DetailView.as_view(), name='detail'),
    ...
]

使用此设置,可以进行以下查找:

  • 如果其中一个实例是最新的 - 例如,如果我们在实例中呈现详细信息页面'author-polls''polls:index'将解析为实例的索引页面'author-polls'; 即以下两个都将导致"/author-polls/"

    在基于类的视图的方法中:

    reverse('polls:index', current_app=self.request.resolver_match.namespace)
    

    并在模板中:

    {% url 'polls:index' %}
    
  • 如果没有当前实例 - 例如,如果我们在网站上的其他位置呈现页面 - 'polls:index'将解析为最后一个注册的实例polls。由于没有默认实例(实例名称空间'polls'),因此polls将使用注册的最后一个实例。这将是'publisher-polls'因为它在最后宣布urlpatterns

  • 'author-polls:index'将始终解析为实例的索引页面 'author-polls'(同样适用于'publisher-polls')。

如果还有一个默认实例 - 即一个名为的实例'polls'- 上面的唯一更改是在没有当前实例的情况下(上面列表中的第二项)。在这种情况下,'polls:index' 将解析为默认实例的索引页,而不是最后声明的实例urlpatterns

URL命名空间和包含的URLconf 

包含的URLconf的应用程序命名空间可以用两种方式指定。

首先,您可以app_name在包含的URLconf模块中设置属性,该属性与属性处于同一级别urlpatterns。您必须将实际模块或对模块的字符串引用传递给它include(),而不是urlpatterns它自己的列表。

民调/的urls.py 

from django.urls import path

from . import views

app_name = 'polls'
urlpatterns = [
    path('', views.IndexView.as_view(), name='index'),
    path('<int:pk>/', views.DetailView.as_view(), name='detail'),
    ...
]

的urls.py 

from django.urls import include, path

urlpatterns = [
    path('polls/', include('polls.urls')),
]

定义的URL polls.urls将具有应用程序命名空间polls

其次,您可以包含一个包含嵌入式命名空间数据的对象。如果include()是列表path()或 re_path()实例,则该对象中包含的URL将添加到全局命名空间。但是,您还可以include()使用包含以下内容的2元组:

(<list of path()/re_path() instances>, <application namespace>)

例如:

from django.urls import include, path

from . import views

polls_patterns = ([
    path('', views.IndexView.as_view(), name='index'),
    path('<int:pk>/', views.DetailView.as_view(), name='detail'),
], 'polls')

urlpatterns = [
    path('polls/', include(polls_patterns)),
]

这将在指定的应用程序命名空间中包含指定的URL模式。

可以使用namespace参数to 指定实例名称空间include()。如果未指定实例名称空间,则它将默认为包含的URLconf的应用程序名称空间。这意味着它也将是该命名空间的默认实例。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值