Django框架之路由层
【一】Django的路由系统
【1】什么是URL配置
- URL调度器 | Django 文档 | Django (djangoproject.com)
- URL配置的本质就是让URL与视图函数之间有对应的关系
- 当在浏览器输入对应的URL,django就能通过URL配置去找对应的视图函数
【2】基本格式
## Django1.x版本语法
from django.conf.urls import url
urlpatterns = [
url(正则表达式, views视图函数,参数,别名),
]
## Django2.x+版本语法
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),
]
【3】URL转换器
- 在Django2.x+版本中,路由层支持URL转换器,可以从url中提取变量,传递到视图函数中。
- 以下是一些常见的URL转换器
- int:匹配零或正整数。
- str:匹配除了斜杠之外的任何非空字符串。
- slug:匹配由ASCII字母或数字,以及连字符和下划线组成的字符串。
- uuid:匹配 UUID 格式的字符串。
- path:匹配任何非空字符串,包括斜杠。
## 这些转换器可以通过在URL模式中使用尖括号来指定,例如:
## urls.py
from django.urls import path
from . import views
urlpatterns = [
path('blog/<int:year>/', views.show_year_archive),
path('blog/<int:year>/<str:month>/', views.show_month_archive),
]
## views.py
from django.http import HttpResponse
def show_year_archive(request, year):
return HttpResponse("Year: " + str(year))
def show_month_archive(request, year, month):
return HttpResponse("Year: " + str(year) + ", Month: " + month)
【4】使用正则表达式
- 在django2.0版本之前,路由层url配置都是用url的,它本身就是支持正则表达式的
- 而在2.0版本之后,关键字变成了path,它是不支持正则表达式的
- 为了兼容老版本项目,需要手动导入
re_path
方法以此支持正则表达式
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),
]
【二】路由匹配
【1】路径参数相似
# 路由匹配
path('test', views.test),
path('testadd', views.testadd),
- 如果路由层这样子配置的话,会发现无论如何都匹配不到testadd对应的内容
- 是因为在匹配路由的时候,是从头到尾的顺序进行的,当输入testadd时,就已经满足了匹配test的条件
- 当匹配成功后就会停止匹配,紧接着执行对应的视图函数
【2】解决办法
# 路由匹配
path('test/', views.test),
path('testadd/', views.testadd),
- 只需要在匹配的路由后面加上一个斜杠即可
- 这样做就意味着匹配的对象必须是以斜杠结尾,这样子testadd就不满足test/的匹配条件了
【3】django自动匹配两次
- 当在网址输入test,或者testadd都会获得到test/或者testadd/的内容
- 这是因为django在匹配路由时会自动匹配两次,第一次匹配就是以浏览器输入的内容为基准
- 如果没有匹配成功,django会自动申请以浏览器输入的内容加上斜杠为条件,进行二次匹配
【4】关闭自动二次匹配
- 如果有特殊需求可以关闭二次匹配的功能,只需要在配置文件加上一行代码即可
APPEND_SLASH = True
【5】首页匹配内容
- 如果没有进程首页URL匹配的话,每次打开127.0.0.1这个地址时都会显示404 not found,这非常不美观
- 只需要把首页对应的路由设置为空即可
path('', views.test),
【三】无名分组
- 分组就是将某段正则表达式用括号括起来
- 当输入的url与正则表达式匹配上后就会传递给视图函数
- 对于无名分组而言,视图函数需要以任意一个形参来接收
# 无名分组
re_path(r'^text/(\d+)/', views.test),
【四】有名分组
- 有名分组在语法上和无名分组不同,使用有名分组时需要给它取一个名字
- 传递给视图函数的时候,视图函数需要用这个名字作为形参接收
# 有名分组
re_path(r'^testadd/(?P<year>\d+)', views.testadd),
【五】反向解析
【1】反向解析的本质
-
本质上是通过一些方法得到一个结果,该结果可以访问到对应的url从而触发相应的视图和功能
-
首先要给路由起一个别名
re_path(r'index/(\d+)/(\d+)/', views.func,name="index"),
【2】后端反向解析
from django.shortcuts import render, HttpResponse,reverse
def home(request):
reverse('index', args=(1, 2)))
【3】前端反向解析
<a href="{% url 'index' 123 234 %}">111</a>
【六】无名有名分组反向解析
【1】有名分组反向路由解析
- 本质上还是通过一些方法得到一个结果,该结果可以访问到对应的url从而触发相应的视图和功能
## urls.py
# 无名分组反向解析
re_path(r'^index/(\d+)/', views.index, name='xxx'),
## views.py
def home(request):
print(reverse('xxx')
return render(request, 'home.html')
## html
<a href="">{% url 'xxx' %}</a>
- 以这样的配置,访问对应的url会发现报错
- 因为路由配置的正则表达式规定了我们需要在后面跟数字
## 解决办法
## views.py
def home(request):
print(reverse('xxx', args=(1,)))
return render(request, 'home.html')
## html
<a href="">{% url 'xxx' 123 %}</a>
【2】案例
- 通常方向解析的意义都是拿到想要的数据
- 如表中的主键
- 比如我在页面上有一张学生信息表的数据,有一个编辑按钮可以编辑每个学生的信息
- 这时候就可以在前端页面的那个数字换成对应学生的ID,然后后端就可以方便快捷的操作对应学生的信息
## urls
re_path(r'^edit_info/(\d+)', views.edit_info, name='edit')
## html
<a href="{% url 'edit' user.id %}" class="btn btn-info btn-xs">编辑</a>
## views.py
def edit_info(requests, args):
user_id = args
user_obj = models.Student.objects.filter(id=user_id).first()
username = user_obj.username
password = user_obj.password
if requests.method == 'POST':
username = requests.POST.get('username')
password = requests.POST.get('password')
user_obj = models.Student.objects.filter(id=user_id)
user_obj.update(username=username, password=password)
return redirect('/user_info/')
return render(requests, 'edit_info.html', locals())
【七】路由分发
【1】前言
- Django项目的每一个应用都可以拥有属于自己的templates文件夹,static文件夹,urls文件
- 正是基于上诉特点,一个django项目可以很方便的做到分组开发,每个人只写自己的app
- 最后只需要将这些app拷贝到一个新项目当中,将app全部都进行注册,
- 在利用路由分发,将每个app的路由对应到根目录下的urls.py文件即可
【2】语法
- 首先要导入include方法
from django.urls import include
urlpatterns = [
path('admin/', admin.site.urls),
path('app1/', include('app1.urls')),
path('app2/', include('app2.urls'))
]
【3】捕获参数
- 目的地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),
]
- 在以上的例子中,当在浏览器输入xxx/blog时,对应的视图函数就可以捕获到xxx的内容,以username作为形参接收
【4】向视图函数传递额外的参数
- 在路由配置后面还可以传一个字典,字典的键将作为视图函数的形参
- 也可以以同样的方式给主路由层传递额外的字典
from django.urls import path
from . import views
urlpatterns = [
path('blog/<int:year>/', views.year_archive, {'foo': 'bar'}),
]
- 在以上的例子中,如果浏览器输入blog/2024
- 对应的视图函数将调用
views.year_archive(request, year='2005', foo='bar')
【八】名称空间
【1】应用命名空间(app_name)
- 当两个app的路由的name属性相同时
- 通过反向解析得到的url就会造成错乱,django不能够识别这两个不同app下的相同路由name
- 这时候就需要名称空间加以区分
【2】使用方法
- 首先在不同的app下的urls.py加一行代码来指定当前app下的名称空间
- 再进入到总路由文件下配置相应的代码
## app1下的urls.py
app_name = 'app01'
## app2下的urls.py
app_name = 'app02'
## 根路由urls.py
urlpatterns = [
path('admin/', admin.site.urls),
path('app1/', include(('app1.urls', 'app1'), namespace='app01')),
path('app2/', include(('app2.urls', 'app2'), namespace='app02'))
]