一个干净优雅
的URL方案
是高质量Web应用
程序中的一个重要细节
。
Django可以让你自己设计URL
,无论你想要什么,没有框架限制。
为了给一个应用设计URL,你需要创建一个Python 模块,通常被称为**URLconf**(URL configuration)
。
这个模块是纯Python代码,是一个简单的Python模式
(简单的正则表达式)到Python函数
(您的视图)之间的映射
。
1. Django是如何处理一个请求?
当用户从Django的站点请求页面时,Django系统遵循以下步骤来执行的Python代码:
-
首先Django确定要使用的根URLconf模块,通过
ROOT_URLCONF
来设置,具体在settings.py
配置文件中。但是如果传入HttpRequest
对象具有urlconf
属性(由中间件设置),则其值将用于替换ROOT\_URLCONF
设置。 -
Django加载该Python模块并查找该变量
urlpatterns
。它是django.urls.path()
和(或)django.urls.re_path()
实例的序列(sequence)。 -
Django按顺序运行每个URL模式,并在匹配所请求的URL的第一个URL中停止。
-
一旦正则表达式匹配,Django将导入并调用给定的视图,这是一个简单的Python函数(或基于类的视图)。该视图会获得如下参数:
- 一个
HttpRequest
实例。 - 如果匹配的正则表达式没有返回任何命名组,那么来自正则表达式的匹配将作为位置参数提供。
- 关键字参数由正则表达式匹配的任何命名组组成,由可选kwargs参数中指定的任何参数覆盖。
django.urls.path\(\)
、django.urls.re_path\(\)
。
- 一个
-
如果没有正则表达式匹配,或者在此过程中的任何一点出现异常,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, '2005', '03')
- /articles/2005/3/ 不符合任何网址格式,因为列表中的第三个条目需要两个数字的月份。
- /articles/2003/将匹配列表中的第一个模式,而不是第二个模式,因为模式是按顺序测试的,第一个模式是第一个测试通过。随意利用这些命令插入特殊情况。在这里,Django会调用该函数 views.special_case_2003(request)
- /articles/2003 将不匹配任何这些模式,因为每个模式要求URL以斜杠结尾。
- /articles/2003/03/03/将匹配最终模式。Django会调用该函数。views.article_detail(request, '2003', '03', '03')
路径转换器
-
默认情况下,以下路径转换器可用:
str
-匹配任何非空字符串,但路径分隔符除外'/'。如果表达式中不包含转换器,则为默认设置。int
-匹配零或任何正整数。返回一个int。slug
-匹配由ASCII字母或数字以及连字符和下划线字符组成的任何条形字符串。例如, building-your-1st-django-site。uuid
-匹配格式化的UUID。为防止多个URL映射到同一页面,必须包含破折号并且字母必须小写。例如,075194d3-6885-417e-a8a8-6c931e272f00。返回一个 UUID实例。path
-匹配任何非空字符串,包括路径分隔符 '/'。这样,您就可以匹配完整的URL路径,而不是像一样匹配URL路径的一部分str。
通过浏览器访问服务
注意:url路由,由上而下 进行匹配,如果在上面就匹配成功,则不会向下匹配
通过浏览器访问服务
127.0.0.1:8000/abc ==> root url(根路由) ==> 加载子路由(myweb/urls.py)
==> 正则匹配访问的路径(path) =-=> 视图函数(views.index)
==> views.py index() 响应内容
使用正则表达式:
-
如果路径和转换器语法不足以定义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})
。 -
不建议特别使用此用法
,因为这样可以更轻松地在匹配的预期含义和视图的参数之间意外引入错误。 -
无论哪种情况,建议在给定的正则表达式中仅使用一种样式。当两种样式混合使用时,任何未命名的组都会被忽略,只有命名的组才会传递到视图函数。
(2).为视图参数指定默认值
- 一个方便的技巧是为视图的参数指定默认参数。这是一个示例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捕获的任何值。
(3). 错误处理
当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。
关于404错误
- 404的错误页面,在模板目录中创建一个404.html的页面,
- 在配置文件中 settings.py 配置 DEBUG = False
- 在配置文件中 settings.py 配置 TEMPLATES = [{'DIRS': [os.path.join(BASE_DIR,'templates')] }]
- 同时需要在项目的根目录下创建文件夹
templates
,并且在此目录下创建一个404.html
文件 - 在出现404的情况时,自动寻找404页面。
- 也可以在视图函数中 手动报出404错误,带提醒信息
在视图函数中也可以指定返回一个404
注意 Http404需要在django.http的模块中引入
# 响应404
raise Http404('纳尼a')
在模板中 404.html
<!DOCTYPE html>
<html>
<head>
<title>404</title>
</head>
<body>
<center>
<h2>404 not found</h2>
<h3>{ { exception } }</h3>
</center>
</body>
</html>
(4). 包括其他的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中以进行进一步处理每当Django遇到
include()
(django.urls.include())时,它会截断与该点匹配的URL的任何部分,并将剩余的字符串发送到包含的URLconf以供进一步处理。
(5). URL的反向解析
如果在视图、模板中使用硬编码的链接,在urlconf发生改变时,维护是一件非常麻烦的事情
- 解决:在做链接时,通过指向urlconf的名称,动态生成链接地址
- 视图:使用django.urls.reverse()函数
- 模板:使用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//。
-
您可以使用以下模板代码获取这些:
<a href="{ % url 'news-year-archive' 2020 % }">2020 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 = 2019
# ...
return HttpResponseRedirect(reverse('news-year-archive', args=(year,)))
- 或简写
from django.shortcuts import redirect
from django.urls import reverse
def index(request):
year = 2019
return redirect(reverse('ews-year-archive',args=(year,)))