URL 调度器 — Django 文档
URL调度器
干净、优雅的 URL 方案是高质量 Web 应用程序中的一个重要细节。 Django 允许您随意设计 URL,没有框架限制。
请参阅 Cool URIs don't change,由万维网创建者 Tim Berners-Lee 撰写,以获取有关为什么 URL 应该干净且可用的极好论据。
概览
要为应用程序设计 URL,您需要创建一个非正式称为 URLconf(URL 配置)的 Python 模块。 该模块是纯 Python 代码,是 URL 路径表达式到 Python 函数(您的视图)之间的映射。
该映射可以根据需要短或长。 它可以引用其他映射。 而且,因为它是纯 Python 代码,所以可以动态构建。
Django 还提供了一种根据活动语言翻译 URL 的方法。 有关更多信息,请参阅 国际化文档 。
Django 如何处理请求
当用户从 Django 驱动的站点请求页面时,系统遵循以下算法来确定要执行的 Python 代码:
- Django 确定要使用的根 URLconf 模块。 通常,这是 :setting:`ROOT_URLCONF` 设置的值,但是如果传入的
HttpRequest
对象具有 urlconf 属性(由中间件设置),则其值将用于代替 :setting:`ROOT_URLCONF` 设置。 - Django 加载该 Python 模块并查找变量
urlpatterns
。 这应该是 django.urls.path() 和/或 django.urls.re_path() 实例的 序列 。 - Django 按顺序运行每个 URL 模式,并在与请求的 URL 匹配的第一个模式处停止。
- 一旦其中一个 URL 模式匹配,Django 就会导入并调用给定的视图,这是一个简单的 Python 函数(或基于 类的视图 )。 该视图传递了以下参数:
- HttpRequest 的一个实例。
- 如果匹配的 URL 模式没有返回命名组,则来自正则表达式的匹配项将作为位置参数提供。
- 关键字参数由与路径表达式匹配的任何命名部分组成,被 django.urls.path() 或 django 的可选
kwargs
参数中指定的任何参数覆盖.urls.re_path()。
- 如果没有 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 路径,而不是像str
那样只匹配 URL 路径的一部分。
注册自定义路径转换器
对于更复杂的匹配要求,您可以定义自己的路径转换器。
转换器是一个包含以下内容的类:
regex
类属性,作为字符串。to_python(self, value)
方法,处理将匹配的字符串转换为应该传递给视图函数的类型。 如果它不能转换给定的值,它应该提高ValueError
。ValueError
被解释为不匹配,因此会向用户发送 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})
。
这种用法不是特别推荐,因为它更容易在匹配的预期含义和视图的参数之间意外引入错误。
无论哪种情况,都建议在给定的正则表达式中仅使用一种样式。 当两种样式混合时,任何未命名的组都将被忽略,并且只有命名的组被传递给视图函数。
嵌套参数
正则表达式允许嵌套参数,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 搜索的内容
URLconf 搜索请求的 URL,作为一个普通的 Python 字符串。 这不包括 GET 或 POST 参数,也不包括域名。
例如,在对 https://www.example.com/myapp/
的请求中,URLconf 将查找 myapp/
。
在对 https://www.example.com/myapp/?page=3
的请求中,URLconf 将查找 myapp/
。
URLconf 不查看请求方法。 换句话说,所有请求方法 - POST
、GET
、HEAD
等等。 – 将路由到相同 URL 的相同函数。
指定视图参数的默认值
一个方便的技巧是为视图的参数指定默认参数。 这是一个示例 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()
函数将使用 num
、1
的默认参数。 如果第二个模式匹配,page()
将使用捕获的任何 num
值。
性能
urlpatterns
中的每个正则表达式在第一次被访问时被编译。 这使得系统非常快。
错误处理
当 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。
包括其他 URLconf
在任何时候,您的 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。
传递额外的选项来查看函数
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}),
]
请注意,额外的选项将 always 传递给包含的 URLconf 中的 every 行,无论该行的视图是否实际接受这些选项为有效。 因此,只有当您确定包含的 URLconf 中的每个视图都接受您传递的额外选项时,此技术才有用。
URL 的反向解析
处理 Django 项目时的一个常见需求是获取最终形式的 URL 的可能性,用于嵌入生成的内容(视图和资产 URL、向用户显示的 URL 等)或用于处理服务器上的导航流侧面(重定向等)
强烈希望避免对这些 URL 进行硬编码(一种费力、不可扩展且容易出错的策略)。 同样危险的是设计临时机制来生成与 URLconf 描述的设计并行的 URL,这可能导致生成的 URL 随着时间的推移变得陈旧。
换句话说,需要的是 DRY 机制。 除了其他优点外,它还允许 URL 设计的演变,而无需遍历所有项目源代码来搜索和替换过时的 URL。
我们可用于获取 URL 的主要信息是标识(例如 负责处理它的视图的名称)。 其他必须参与正确 URL 查找的信息是视图参数的类型(位置、关键字)和值。
Django 提供了一种解决方案,使 URL 映射器成为 URL 设计的唯一存储库。 你用你的 URLconf 提供它,然后它可以在两个方向使用:
- 从用户/浏览器请求的 URL 开始,它调用正确的 Django 视图,提供它可能需要的任何参数,以及从 URL 中提取的值。
- 从相应 Django 视图的标识以及将传递给它的参数值开始,获取关联的 URL。
第一个是我们在前几节中讨论的用法。 第二个是所谓的 URL 反向解析 、 反向 URL 匹配 、 反向 URL 查找 ,或简称为 URL 反向 。
Django 提供了用于执行 URL 反转的工具,这些工具匹配需要 URL 的不同层:
- 在模板中:使用 :ttag:`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/<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 和大多数第三方应用程序假定此视图具有名称为 login
的 URL 模式。 如果您有自定义登录视图并将其 URL 命名为 login
,则 reverse() 将找到您的自定义视图,只要它位于 urlpatterns
之后 [ X154X] 包含在内(如果包含的话)。
如果多个 URL 模式的参数不同,您也可以对它们使用相同的名称。 除了 URL 名称,reverse() 匹配参数的数量和关键字参数的名称。
网址命名空间
简介
URL 命名空间允许您唯一地反转 命名的 URL 模式 ,即使不同的应用程序使用相同的 URL 名称。 对于第三方应用程序来说,始终使用命名空间 URL 是一个很好的做法(正如我们在教程中所做的那样)。 同样,如果部署了应用程序的多个实例,它还允许您反转 URL。 换句话说,由于单个应用程序的多个实例将共享命名 URL,命名空间提供了一种区分这些命名 URL 的方法。
可以为特定站点多次部署正确使用 URL 命名空间的 Django 应用程序。 例如 django.contrib.admin 有一个 AdminSite 类,它允许您轻松地 部署多个 admin 实例。 在后面的示例中,我们将讨论在两个不同位置部署本教程中的民意调查应用程序的想法,以便我们可以为两个不同的受众(作者和出版商)提供相同的功能。
URL 命名空间分为两部分,都是字符串:
- 应用程序命名空间
- 这描述了正在部署的应用程序的名称。 单个应用程序的每个实例都将具有相同的应用程序命名空间。 例如,Django 的管理应用程序有一个有点可预测的应用程序命名空间
'admin'
。 - 实例命名空间
- 这标识了应用程序的特定实例。 实例命名空间在整个项目中应该是唯一的。 但是,实例命名空间可以与应用程序命名空间相同。 这用于指定应用程序的默认实例。 例如,默认的 Django 管理实例的实例命名空间为
'admin'
。
命名空间 URL 使用 ':'
运算符指定。 例如,使用 'admin:index'
引用管理应用程序的主索引页面。 这表示 'admin'
的命名空间和 'index'
的命名 URL。
命名空间也可以嵌套。 命名的 URL 'sports:polls:index'
将在命名空间 'polls'
中查找名为 'index'
的模式,该模式本身在顶级命名空间 'sports'
中定义。
反转命名空间 URL
当给定一个命名空间的 URL 时(例如 'polls:index'
) 来解析,Django 将完全限定名称拆分为多个部分,然后尝试以下查找:
首先,Django 查找匹配的 应用程序命名空间 (在本例中为
'polls'
)。 这将产生该应用程序的实例列表。如果定义了当前应用程序,Django 会查找并返回该实例的 URL 解析器。 可以使用 reverse() 函数的
current_app
参数指定当前应用程序。:ttag:`url` 模板标签使用当前解析视图的命名空间作为 RequestContext 中的当前应用程序。 您可以通过在 request.current_app 属性上设置当前应用程序来覆盖此默认值。
如果当前没有应用程序,Django 会寻找一个默认的应用程序实例。 默认应用程序实例是具有与 应用程序命名空间 匹配的 实例命名空间 的实例(在此示例中,名为
'polls'
的polls
实例) .如果没有默认的应用程序实例,Django 将选择最后部署的应用程序实例,不管它的实例名称是什么。
如果提供的命名空间与步骤 1 中的 应用程序命名空间 不匹配,Django 将尝试将该命名空间直接查找为 实例命名空间 。
如果有嵌套的命名空间,则对命名空间的每个部分重复这些步骤,直到只有视图名称未解析。 然后,视图名称将被解析为已找到的命名空间中的 URL。
示例
为了展示此解析策略的实际效果,请考虑本教程中 polls
应用程序的两个实例:一个称为 'author-polls'
,另一个称为 'publisher-polls'
。 假设我们已经增强了该应用程序,以便在创建和显示投票时考虑实例命名空间。
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')),
]
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'
实例的索引页面; IE 以下两种情况都会导致"/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 的应用程序命名空间可以通过两种方式指定。
首先,您可以在包含的 URLconf 模块中设置 app_name
属性,与 urlpatterns
属性处于同一级别。 您必须将实际模块或对该模块的字符串引用传递给 include(),而不是 urlpatterns
本身的列表。
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'),
...
]
from django.urls import include, path
urlpatterns = [
path('polls/', include('polls.urls')),
]
polls.urls
中定义的 URL 将有一个应用程序命名空间 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() 的 namespace
参数指定实例命名空间。 如果未指定实例命名空间,它将默认为包含的 URLconf 的应用程序命名空间。 这意味着它也将是该命名空间的默认实例。