• 博客园logo
  • 会员
  • 周边
  • 新闻
  • 博问
  • 闪存
  • 众包
  • 赞助商
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
菩提叶子
博客园    首页    新随笔    联系   管理    订阅  订阅
django路由系统

一、Django如何处理请求

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

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

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

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

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

一个实例HttpRequest。

如果匹配的 URL 模式不包含命名组,则来自正则表达式的匹配项作为位置参数提供。

关键字参数由与提供的路径表达式匹配的任何命名部分组成,被or 的可选kwargs参数中指定的任何参数覆盖。django.urls.path()django.urls.re_path()如果没有匹配的 URL 模式,或者在此过程中的任何时候引发异常。

二、示例

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。

三、路径转换器

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

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

1、注册自定义路径转换器

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

  • 一个regex类属性,作为一个字符串。
  • 一种方法,它处理将匹配的字符串转换为应该传递给视图函数的类型。如果它不能转换给定的值,它应该引发。A被解释为不匹配,因此除非另一个 URL 模式匹配,否则将向用户发送 404 响应。to_python(self, value)ValueErrorValueError
  • 一种方法,它处理将 Python 类型转换为要在 URL 中使用的字符串。如果它不能转换给定的值,它应该引发。A被解释为不匹配,因此除非另一个 URL 模式匹配,否则将引发 。to_url(self, value)ValueErrorValueErrorreverse()NoReverseMatch
class FourDigitYearConverter:
    regex = '[0-9]{4}'

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

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

使用以下方法在 URLconf 中注册自定义转换器类 register_converter():

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

2、使用正则表达式

如果路径和转换器语法不足以定义您的 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),
]

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

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

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

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

3.1、嵌套参数

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

from django.urls import re_path

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

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

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

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

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

四、URLconf搜索的内容

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

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

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

1、指定视图参数的默认值

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):
    pass

在上面的例子中,两个 URL 模式都指向同一个视图—— views.page但是第一个模式没有从 URL 中捕获任何东西。如果第一个模式匹配,该page()函数将使用其默认参数num, 1。如果第二个模式匹配, page()将使用num捕获的任何值。

 

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

2、其他URLconfs

urlpatterns都可以“包含”其他 URLconf 模块。这实质上是“根”在其他 URL 之下的一组 URL。

例如

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 视图处理。

这可用于从重复使用单个模式前缀的 URLconfs 中删除冗余。例如,考虑这个 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),
    ])),
]

3、传递额外的选项查看函数

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

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

例如:

from django.urls import path
from . import views

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

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

from django.urls import include, path

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

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

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

五、URL反向解析

1、概念

从相应的 Django 视图的标识以及将传递给它的参数值开始,获取关联的 URL,就是URL反向解析

2、使用方法

2.1、在模板中:使用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>/

2.2、使用reverse()函数

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命名空间

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

当给定一个命名空间的 URL(例如'polls:index')来解析时,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。

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

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

首先,您可以app_name在包含的 URLconf 模块中设置一个属性,与该属性处于同一级别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')),
]

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

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

(<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参数 指定实例命名空间include()。如果未指定实例命名空间,它将默认为包含的 URLconf 的应用程序命名空间。这意味着它也将是该命名空间的默认实例。

九、URLconfs函数

1、path

#path()
#path(路线,视图,kwargs =无,名称=无)

from django.urls import include, path

urlpatterns = [
    path('index/', views.index, name='main-view'),
    path('bio/<username>/', views.bio, name='bio'),
    path('articles/<slug:title>/', views.article, name='article-detail'),
    path('articles/<slug:title>/<int:section>/', views.section, name='article-section'),
    path('blog/', include('blog.urls')),
    ...
]

route参数应该是 包含 URL模式的字符串或gettext_lazy(),该字符串可能包含尖括号(<username>如上)以捕获部分 URL 并将其作为关键字参数发送到视图。尖括号可能包括一个转换器规范(如 的int一部分<int:section>),它限制匹配的字符,也可能更改传递给视图的变量的类型。例如,<int:section>匹配一串十进制数字并将值转换为int.

view参数是视图函数或 as_view()基于类的视图的结果。它也可以是一个django.urls.include().

该kwargs参数允许您将其他参数传递给视图函数或方法。

2、re_path()

#re_path(路线,视图,kwargs =无,名称=无)

from django.urls import include, re_path

urlpatterns = [
    re_path(r'^index/$', views.index, name='index'),
    re_path(r'^bio/(?P<username>\w+)/$', views.bio, name='bio'),
    re_path(r'^blog/', include('blog.urls')),
    ...
]

route参数应该是一个字符串或 gettext_lazy(),其中包含与 Python re模块兼容的正则表达式。字符串通常使用原始字符串语法 ( r''),因此它们可以包含类似\d的序列,而无需使用另一个反斜杠来转义反斜杠。当匹配成功时,从正则表达式中捕获的组被传递给视图——如果组被命名,则作为命名参数,否则作为位置参数。这些值作为字符串传递,没有任何类型转换。

当 aroute以$整个请求的 URL 结尾时,匹配 path_info, 必须匹配正则表达式模式 (re.fullmatch()使用)。

,view和参数与 for kwargs相同

3、include()

#include(模块,命名空间=无)
#include(模式列表)
#include( (pattern_list , app_namespace) , namespace=None )

一个函数,它将完整的 Python 导入路径带到另一个应该“包含”在此位置的 URLconf 模块。可选地,还可以指定将包含条目的应用程序名称空间和实例名称空间。

通常,应用程序命名空间应由包含的模块指定。如果设置了应用程序命名空间,则该namespace参数可用于设置不同的实例命名空间。

include()还接受返回 URL 模式的可迭代对象或包含此类可迭代对象以及应用程序命名空间名称的 2 元组作为参数。

参数:
  • module – URLconf 模块(或模块名称)
  • namespace ( str ) – 包含的 URL 条目的实例命名空间
  • pattern_list – 可迭代的path()和/或re_path()实例。
  • app_namespace ( str ) – 包含的 URL 条目的应用程序命名空间

4、register_converter()

register_converter(转换器, type_name ) 

注册转换器以在s 中使用的函数。path() route

converter参数是一个转换器类,并且type_name是在路径模式中使用的转换器名称

十、django.conf.urlsURLconfs使用的函数

1、static()

#static.static(前缀,视图= django.views.static.serve,** kwargs)


from django.conf import settings
from django.conf.urls.static import static

urlpatterns = [
    # ... the rest of your URLconf goes here ...
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

 

 

 

posted on 2022-11-08 16:34  菩提叶子  阅读(64)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3