zoukankan      html  css  js  c++  java
  • django学习笔记(五)------path

    从’https://docs.djangoproject.com/en/3.0/topics/http/urls/ ‘ 翻译来

    Django如何处理请求

    当用户从Django支持的站点请求页面时,系统将遵循以下算法来确定要执行的Python代码:

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

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

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

    4. 一旦其中一个URL模式匹配,Django就会导入并调用给定的视图,该视图是一个Python函数(或基于类的视图)。该视图传递了以下参数:
      (1) HttpRequest的一个实例。
      (2)如果匹配的URL模式不包含命名组,则正则表达式中的匹配将作为位置参数提供。
      (3)关键字参数由与提供的路径表达式匹配的任何命名部分组成,由可选的kwargs参数中指定的任何参数重写为django.urls.path()或django.urls.re_path()。
      (4)在Django 3.0中更改:
      在旧版本中,没有值的关键字参数也用于未提供的命名部分。

    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捕获整数参数。如果不包括转换器,则匹配除a/字符之外的任何字符串。
    • 不需要添加前导斜杠,因为每个URL都有前导斜杠。例如,它是文章,而不是/文章。

    示例请求:

    • 对/articles/2005/03/的请求将匹配列表中的第三个条目。Django将调用函数views.month_archive(请求,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路径,而不是像str那样匹配URL路径的一段。

    注册自定义路径转换器

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

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

    • regex类属性,作为字符串。
    • to_python(self,value)方法,用于将匹配的字符串转换为应传递给view函数的类型。如果无法转换给定值,则会引发ValueError。ValueError被解释为不匹配,因此,除非另一个URL模式匹配,否则会向用户发送404响应。
    • 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 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),
    ]

    这与上一个示例实现了大致相同的功能,除了:

    • 将要匹配的确切url稍微有点受限。例如,年份10000将不再匹配,因为年份整数被限制为正好四位数长。
    • 每个捕获的参数都以字符串的形式发送到视图,而不管正则表达式的匹配类型如何。

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

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

    以及命名组语法,例如(?P< year>[0-9]{4}),也可以使用较短的未命名组,例如([0-9]{4})。

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

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

    嵌套参数

    正则表达式允许嵌套参数,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/或无参数,而注释可以用无参数或页面号的值反转。

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

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

    URLconf搜索的内容

    URLconf作为一个普通的Python字符串对请求的URL进行搜索。这不包括GET或POST参数或域名。

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

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

    URLconf不查看请求方法。换句话说,所有请求方法(POST、GET、HEAD等)都将被路由到同一个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值。

    表现
    urlpatterns中的每个正则表达式都是在第一次访问时编译的。这使得系统速度惊人。

    urlpatterns变量的语法

    urlpatterns应该是path()和/或re_path()实例的序列。

    错误处理

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

    用于这些情况的视图由四个变量指定。它们的默认值应该可以满足大多数项目的需要,但是可以通过重写它们的默认值来进行进一步的自定义。 有关详细信息,请参阅有关自定义错误视图的文档。

    这些值可以在根URLconf中设置。在任何其他URLconf中设置这些变量都没有效果。

    值必须是可调用的,或者是表示到视图的完整Python导入路径的字符串,应该调用该路径来处理手头的错误条件。

    变量包括:

    handler400–请参阅django.conf.urls.handler400。
    handler403–请参阅django.conf.urls.handler403。
    handler404–请参阅django.conf.urls.handler404。
    handler500–请参阅django.conf.urls.handler500。

    包括其他URLconfs

    在任何时候,urlpatterns都可以“包括”其他URLconf模块。这本质上是“根”一组位于其他URL下面的URL。

    例如,这里是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),
        ])),
    ]

    捕捉参数

    包含的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”变量按预期传递给包含的URLconf。

    传递额外选项以查看函数

    urlconf有一个钩子,可以将额外的参数作为Python字典传递给视图函数。

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

    例如:

    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的生成随着时间的推移而过时。

    换句话说,我们需要的是一个干燥的机制。除其他优点外,它将允许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'),
        #...
    ]

    根据这种设计,nnnn年对应的存档的URL是/articles//。 您可以使用以下方法在模板代码中获取这些信息:

    <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模式注释而另一个应用程序执行相同的操作,reverse()找到的URL取决于项目的urlpatterns列表中最后一个模式。

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

    如果要覆盖视图,可以故意选择与其他应用程序相同的URL名称。例如,一个常见的用例是重写LoginView。Django和大多数第三方应用程序的一部分都假设这个视图有一个名为login的URL模式。如果您有一个自定义登录视图,并将其URL命名为login,reverse()将在包含django.contrib.auth.urls之后(如果包含的话)在urlpatterns中找到您的自定义视图。

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

    URL命名空间

    导言

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

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

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

    应用程序命名空间

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

    实例命名空间

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

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

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

    反转命名空间URL

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

    1. 首先,Django查找匹配的应用程序命名空间(在本例中为“polls”)。这将生成该应用程序的实例列表。
    2. 如果定义了当前应用程序,Django将查找并返回该实例的URL解析器。可以使用reverse()函数的current_app参数指定当前应用程序。
      url模板标记使用当前解析视图的命名空间作为RequestContext中的当前应用程序。您可以通过在request.current_app属性上设置当前应用程序来覆盖此默认值。
    3. 如果没有当前应用程序,Django将查找默认应用程序实例。默认应用程序实例是具有与应用程序命名空间匹配的实例命名空间的实例(在本例中,是一个名为“polls”的polls实例)。
    4. 如果没有默认的应用程序实例,Django将选择应用程序的最后一个部署实例,不管它的实例名是什么。
    5. 如果在步骤1中提供的命名空间与应用程序命名空间不匹配,Django将尝试将命名空间直接查找为实例命名空间。

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

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

    #url.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')),
    ]
    #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命名空间和包含的URLconfs

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

    首先,可以在包含的URLconf模块中设置app_name属性,其级别与urlpatterns属性相同。必须传递实际的模块或对模块的字符串引用才能include(),而不是urlpatterns本身的列表。

    #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'),
        ...
    ]
    #urls.py
    from django.urls import include, path
    urlpatterns = [
        path('polls/', include('polls.urls')),
    ]

    polls.url中定义的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模式。

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

  • 相关阅读:
    初识Java内存结构
    eclipse的安装与配置
    关于android客户端使用ksoap2调用wcf(.svc)的总结
    ie下jpg不显示问题
    Android学习笔记(1)
    HTML标签语义化
    【转】android模拟机不能上网
    WCF IIS 寄宿问题
    C# 参数传递
    Wcf IIS 寄宿
  • 原文地址:https://www.cnblogs.com/llb123/p/13398717.html
Copyright © 2011-2022 走看看