Django的URLconf

时间:2023-01-10 11:08:16

URL

概要

我们要在Django项目中为应用程序设计URL,我们可以创建一个名为URLconf(通常为urls.py)的Python模块。
这个模块是纯Python代码,是一个简单的正则表达式到Python函数(视图)之间的映射。

这种映射关系可以很简短也可以很复杂。它还可以引用其他的映射关系。

Django如何处理一个请求

如果用户请求一个由Django提供服务的站点,它将按照以下逻辑决定执行哪些代码:

  1. 通常不考虑中间件的情况下,Django将会确定要使用的根URLconf模块。
  2. Django加载该Python模块并查找变量--urlpatterns,这个变量应该是一个由django.conf.urls.url()实例组成的列表。
  3. Django按照顺序遍历每一个URL模式,并停在与本次请求的URL匹配的第一个URL模式。
  4. 一旦一个正则表达式匹配上用户请求的URL,Django就会导入并调用给定的视图,该视图是一个简单的Python函数(或基于类的视图)。
    该视图将被传入以下参数:
    • 一个请求示例--request
    • 正则表达式中使用分组捕获的值将作为位置参数传递给视图。
    • 正则表达式中的命名分组捕获的值将会以关键字方式传递给视图,但是它可以被直接传参方式覆盖。
  5. 如果没有正则表达式匹配或者在此过程中发生了异常,Django都会调用适当的错误处理视图。

注意:

  1. 分组和命名分组不能同时使用。

    请求URL:
    http://127.0.0.1:8000/kwargs_test/123/abc/

    url(r'^kwargs_test/(\d+)/(?P<name>\w+)/', views.kwargs_test),

    视图:

    def kwargs_test(request, *args, **kwargs):
    print(args, kwargs)
    return HttpResponse("OK")

    输出:

    () {'name': 'abc',}
  2. django.conf.urls.url()的关键字参数会覆盖正则表达式中命名分组捕获的值。

    请求URL:
    http://127.0.0.1:8000/kwargs_test/123/abc/

    url(r'^kwargs_test/(\d+)/(?P<name>\w+)/', views.kwargs_test, {"name1": "Q1mi"}),

    视图:

    def kwargs_test(request, *args, **kwargs):
    print(args, kwargs)
    return HttpResponse("OK")

    输出:

    () {'name': 'abc', 'name1': 'Q1mi'}

小总结:
我们现在掌握了三种向视图函数传递参数的方法:

  1. 在正则匹配模式中使用分组模式从请求的URL中捕获参数并以位置参数的形式传递给视图。
  2. 在正则匹配模式中使用命名分组模式从请求的URL中捕获参数并以关键字参数的形式传递给视图。
  3. 通过给django.conf.urls.url()传递参数。

反向解析URL

在我们的Django项目中,我们经常会遇到要在视图中返回一个重定向到具体URL的响应,或者要将具体的URL地址嵌入到HTML页面中(如:a标签的href属性等)的情况。

我们之前通常都会选择硬编码(写死)的方式来实现类似上述的需求,但是这并不是最优的解决办法。
例如:
在视图函数中:

def add_student(request):
if request.method == "POST":
...
return redirect("/student_list/") # 将URL硬编码到视图中
...

在模板文件的HTML文件中:

<a href="/student_list/">点击查看所有学生信息</a>

Django框架充分考虑了这种需求,所以提供了工具来反向解析(推导)出具体的URL。

name模式

在Django的URLconf中,我们可以通过给匹配模式起别名,然后我们可以通过别名来反向推导出具体的URL。

例如:

在urls.py中:

urlpatterns = [
url(r'^student_list/', views.student_list, name="students"), # 为匹配模式起别名
...
]

在视图中:
通过使用django.urls.reverse根据上面的别名反向推导出URL。

from django.urls import reverse
def add_student(request):
if request.method == "POST":
...
# 根据别名反向推导出具体的URL,避免出现硬编码URL的情况。
url = reverse("students") # 得到URL: /student_list/
return redirect(url)
...

在模板语言的HTML文件中,使用下面的语法来反向推导出URL:

<a href="{% url 'students' %}">点击查看所有学生信息</a>

当我们的匹配模式中的正则表达式有分组或命名分组的时候该怎么办呢?
也就是我们仅仅通过别名无法推导出具体的URL时,该怎么办呢?

我们可以在反向推导URL的时候传递URL中需要的参数:

URL中需要位置参数

例如:
在urls.py中:

urlpatterns = [
# 为匹配模式起别名,并且正则表达式中有分组匹配
url(r'^student/(\d+)', views.student_detail, name="student_detail"),
...
]

像上面的情况,我们就没法简单的通过别名来推导出具体的URL。
我们在反向推导URL的时候就需要传递参数了。

在视图中:
通过使用django.urls.reverse根据上面的别名反向推导出URL。

from django.urls import reverse
def add_student(request):
if request.method == "POST":
...
# 根据别名和位置参数反向推导出具体的URL,避免出现硬编码URL的情况。
url = reverse("student_detail", args=(1,)) # 得到URL:/student/1/
return redirect(url)
...

在模板语言的HTML文件中,使用下面的语法来反向推导出URL:

<a href="{% url 'student_detail' 1 %}">点击查看学生详细信息</a>

URL中需要关键字参数

例如:
在urls.py中:

urlpatterns = [
# 为匹配模式起别名,并且正则表达式中有分组匹配
url(r'^student/(?P<num>\d+)', views.student_detail, name="student_detail"),
...
]

像上面的情况,我们就没法简单的通过别名来推导出具体的URL。
我们在反向推导URL的时候就需要传递参数了。

在视图中:
通过使用django.urls.reverse根据上面的别名反向推导出URL。

from django.urls import reverse
def add_student(request):
if request.method == "POST":
...
# 根据别名和位置参数反向推导出具体的URL,避免出现硬编码URL的情况。
url = reverse("student_detail", kwargs={"num": 10}) # 得到URL:/student/10/
return redirect(url)
...

在模板语言的HTML文件中,使用下面的语法来反向推导出URL:

<a href="{% url 'student_detail' num=10 %}">点击查看学生详细信息</a>

namespace模式

我们可以为每个URL的匹配模式设置一个别名,然后通过别名来反向推导出URL。这样的设置在小型项目也就是URL比较少的情况下是完全够用的。
但是一旦我们的项目比较庞大,其URL可能成百上千,不可避免的会出现别名重复的情况。

这个时候就需要使用namespace了,我们可以为不同的urlpatterns设置一个namespace(命名空间),这样在不同的命名空间下即使别名相同,还是可以通过namespace来区分不同的URL匹配模式。

在urls.py中:

urlpatterns = [
...
# 为app01.urls设置命名空间名:beijing
url(r'^beijing/', include("app01.urls", namespace="beijing")),
# 为app02.urls设置命名空间名:shanghai
url(r'^shanghai/', include("app02.urls", namespace="shanghai")),
...
]

app01/urls.py

urlpatterns = [
# app01/urls.py中有一个别名为index的匹配模式
url(r'^index/$', views.index, name="index"),
]

app02/urls.py

urlpatterns = [
# app02/urls.py中也有一个别名为index的匹配模式
url(r'^index/$', views.index, name="index"),
]

现在我们就可以通过namespace来区分上面两个别名为index的匹配模式来反向推导出准确的URL了。

在视图中:

def index(request):
# 通过 namespce:name 的方式来反向推导出准确的URL
url = reverse("shanghai:index")
...

在模板语言的HTML中:

<a href="{% url 'shanghai:index' %}">上海分公司首页</a>

也可以通过在app/urls.py中定义app_name来设置app级别的namespace

例如:
在上面示例的app01/urls.py文件中:

app_name = "beijing"
urlpatterns = [
url(r'^index/$', views.index, name="index"),
...
]

在这种情况下反向推导URL的语法同上。