Django URL分发 & 反向解析
一、URL分发
1、Django如何处理一个请求
- Django决定要使用的根URLConf,这个根URLConf通常是由ROOT_URLCONF设置的,但是如果传入的HttpRequest对象有urlconf参数的话,它会取代ROOT_URLCONF。
- Django会读取
urlpatterns
变量,变量应该是django.conf.urls.url()
对象的列表集合 - Django会逐条匹配URL,会返回匹配到的第一条请求URL
- 一旦成功匹配了正则表达式,Django会引入对应的view方法,view的参数如下:
- HttpRequest对象
- 如果匹配到的正则表达式没有返回命名组,则正则表达式中的匹配项作为位置参数提供
- 关键字参数由匹配到的正则表达式中的命名组组成, 并且通过kwargs参数重写
django.conf.urls.url()
- 如果没有匹配的正则表达式,或者在处理过程中抛出了异常,Django会调用适当的错误处理视图
2、注意点
2.1 分组和命名分组不能同时使用
# 请求URL
http://127.0.0.1:8000/kwargs_test/123/abc/
# urls.py
url(r'^kwargs_test/(\d+)/(?P<name>\w+)/', views.kwargs_test)
# 视图views.py
def kwargs_test(request, *args, **kwargs):
print(args, kwargs)
return HttpResponse("OK")
# 输出
() {'name': 'abc',}
2.2 django.conf.urls.url()的关键字参数会覆盖正则表达式中命名分组捕获的值
#请求URL
http://127.0.0.1:8000/kwargs_test/123/abc/
# urls.py
url(r'^kwargs_test/(\d+)/(?P<name>\w+)/', views.kwargs_test, {"new_name": "Yang"}),
# 视图views.py
def kwargs_test(request, *args, **kwargs):
print(args, kwargs)
return HttpResponse("OK")
# 输出
() {'name': 'abc', 'name1': 'Q1mi'}
2.3 向视图函数传递参数三种方法
- 在正则匹配模式中使用
分组模式
从请求的URL中捕获参数并以位置参数
的形式传递给视图 - 在正则匹配模式中使用
命名分组模式
从请求的URL中捕获参数并以关键字参数
的形式传递给视图 - 通过给
django.conf.urls.url()
传递参数
二、URL反向解析
在我们的Django项目中,我们经常会遇到要在视图中返回一个重定向到具体URL的响应,或者要将具体的URL地址嵌入到HTML页面中(如:a标签的href属性等)的情况,Django框架充分考虑了这种需求,所以提供了工具来反向解析(推导)出具体的URL。
1. name模式
在Django的URLconf中,我们可以通过给匹配模式起别名,然后我们可以通过别名来反向推导出具体的URL。
1.1 普通情况
# urls.py
urlpatterns = [
# 为匹配模式起别名
url(r'^student_list/', views.student_list, name="students"),
]
# 视图views.py中通过使用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中
<a href="{% url 'students' %}">点击查看所有学生信息</a>
1.2 URL中需要位置参数
# urls.py
urlpatterns = [
# 为匹配模式起别名,并且正则表达式中有分组匹配
url(r'^student/(\d+)', views.student_detail, name="student_detail"),
]
# 视图views.py中通过使用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中
<a href="{% url 'student_detail' 1 %}">点击查看学生详细信息</a>
1.3 URL中需要关键字参数
# urls.py
urlpatterns = [
# 为匹配模式起别名,并且正则表达式中有分组匹配
url(r'^student/(?P<num>\d+)', views.student_detail, name="student_detail"),
]
# 视图views.py中通过使用django.urls.reverse根据上面的别名反向推导出URL
from django.urls import reverse
def add_student(request):
if request.method == "POST":
# 根据别名和位置参数反向推导出具体的URL,避免出现硬编码URL的情况。
# 得到URL:/student/10/
url = reverse("student_detail", kwargs={"num": 10})
return redirect(url)
# HTML中
<a href="{% url 'student_detail' num=10 %}">点击查看学生详细信息</a>
2. namespace模式
我们的项目比较庞大,其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"),
]
# 视图views.py中
def index(request):
# 通过 namespce:name 的方式来反向推导出准确的URL
url = reverse("shanghai:index")
# HTML中
<a href="{% url 'shanghai:index' %}">上海分公司首页</a>
2.1 也可以通过在app/urls.py中定义app_name
来设置app级别的namespace
# 在上面示例的app01/urls.py文件中:
app_name = "beijing"
urlpatterns = [
url(r'^index/$', views.index, name="index"),
]