Django路由层
一、MVC和MTV框架
MVC
- M 代表模型(Model)
- V 代表视图(View)
- C 代表控制器(Controller)
Web服务器开发领域里著名的MVC模式,所谓MVC就是把Web应用分为模型(M),控制器(C)和视图(V)三层,他们之间以一种插件式的、松耦合的方式连接在一起,模型负责业务对象与数据库的映射(ORM),视图负责与用户的交互(页面),控制器接受用户的输入调用模型和视图完成用户的请求。
MTV
Django 的 MTV 模式本质上和MVC是一样的,也是为了各组件间保持松耦合关系,只是定义上有些许不同,Django 的MTV分别是值:
- M 代表模型(Model): 负责业务对象和数据库的关系映射(ORM)。
- T 代表模板 (Template):负责如何把页面展示给用户(html)。
- V 代表视图(View): 负责业务逻辑,并在适当时候调用Model和Template。
除了以上三层之外,还需要一个URL分发器,它的作用是将一个个URL的页面请求分发给不同的View处理,View 再调用相应的 Model 和 Template
二、URL配置
URL配置:它的本质是URL与要为该URL调用的视图函数之间的映射表。
Django 1.x版本
url()方法:普通路径和正则路径均可使用,需要自己手动添加正则首位限制符号。
注意:
- urlpatterns中的元素按照书写顺序从上往下逐一匹配,一旦匹配成功则不在继续
- 若要从URL中捕获一个值,只需要在它周围放置一对圆括号(分组匹配)。
# urls.py文件
from django.contrib import admin
from django.conf.urls import url # 2.x之后,用 url 需要引用
from app01 import views # 需要自行导入视图文件 views.py
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^index/', views.index), # 新增一条
]
# ------------------------------------------------------------------
# views.py文件
from django.shortcuts import render
# 新增视图函数
def index(request): # request 中包含请求信息
return render(request, 'index.html') # 返回的 HTML 页面
Django 2.x之后版本
path:用于普通路径,不需要自己手动添加正则首位限制符号,底层已经添加。
re_path:用于正则路径,需要自己手动添加正则首位限制符号。
# urls.py文件
from django.contrib import admin
from django.urls import path, re_path
from app01 import views
urlpatterns = [
path('admin/', admin.site.urls),
path('index/', views.index), # 新增-普通路径
re_path(r'^books/\d{4}/$', views.books) # 新增-正则路径
]
# -------------------------------------------------------------------
# views.py文件
from django.shortcuts import render, HttpResponse # 导入HttpResponse,用来生成响应信息
def index(request):
return render(request, 'index.html') # 返回的 HTML 页面
def books(request):
return HttpResponse('Hello world') # 响应信息
三、分组
分组就是需要直接从路径中取出参数,这就用到了正则表达式的分组功能了。
分组分为两种:无名分组与有名分组
无名分组
无名分组按照位置传参,需要一 一对应。
捕获URL中的值并以位置参数形式传递给视图。
views 中除了request,其他形参的数量要与urls中的分组数量一致。
# ruls.py文件
from django.contrib import admin
from django.urls import path, re_path
from app01 import views
urlpatterns = [
re_path(r'^admin/', admin.site.urls),
re_path(r'^books/(\d{4})/', views.books),
]
# ------------------------------------------------------------------
# views.py文件
from django.shortcuts import render, HttpResponse
def books(request, year):
print(year) # 一个形参代表路径中一个分组的内容,按顺序匹配
return HttpResponse(f'Hello world {year}')
有名分组
语法:
(?P<组名>正则表达式)
捕获URL中的值并以关键字参数形式传递给视图。
有几个有名分组就要有几个关键字参数
# urls.py文件
from django.contrib import admin
from django.urls import path, re_path
from app01 import views
urlpatterns = [
re_path(r'^admin/', admin.site.urls),
# 匹配成功的分组部分会以关键字参数(name_id = 匹配成功的数字)的形式传给视图函数。
re_path(r'^books/(?P<name_id>\d{2})/', views.books),
]
# ------------------------------------------------------------------
# views.py文件
from django.shortcuts import render, HttpResponse
# 需要额外增加一个形参,形参名必须为 name_id
def books(request, name_id):
return HttpResponse(f'Hello world {name_id}')
总结:有名分组和无名分组都是为了获取路径中的参数,并传递给视图函数,区别在于无名分组是以位置参数的形式传递,有名分组是以关键字参数的形式传递。
四、路由分发(include)
存在的问题:Django项目里有多个app共用一个 urls 容易造成混淆,后期维护不方便。
解决:使用路由分发(include),让每个app目录都单独拥有自己的 urls。
步骤:
1、在各自 app 目录下的创建 urls.py文件, 并对 urls.py 文件和 views.py 文件中写各自的路由和视图函数。
app01下的 urls.py 文件和 views.py 文件
# urls.py文件
from django.urls import path, re_path
from app01 import views # 导入app01的views
urlpatterns = [
re_path(r'^index/', views.index),
]
# ------------------------------------------------------------------
# views.py文件
from django.shortcuts import render, HttpResponse
def index(request):
return HttpResponse('app01页面')
app02下的 urls.py 文件和 views.py 文件
# urls.py文件
from django.urls import path, re_path
from app02 import views # 导入app02的views
urlpatterns = [
re_path(r'^index/', views.index),
]
# ------------------------------------------------------------------
# views.py文件
from django.shortcuts import render, HttpResponse
def index(request):
return HttpResponse('app02页面')
2、在项目名称目录下的 urls 文件里面,统一将路径分发给各个 app 目录。
from django.contrib import admin
from django.urls import path, re_path, include
# 总路由表
urlpatterns = [
re_path(r'^admin/', admin.site.urls),
# 新增两条路由,不能以$结尾
# include函数就是做分发操作的。
re_path(r'^app01/', include('app01.urls')),
re_path(r'^app02/', include('app02.urls')),
]
五、反向解析
如果路由 层的 url 发生变化,就需要取更改对应的视图层和模板层的 url ,非常麻烦,不便于维护。
所以可以利用反向解析,当路由层 url 发生变化,在视图层和模块层动态反向解析出更改后的 url ,避免修改操作
反向解析一般用在模板中的超链接以及视图中的重定向。
普通反向解析
登录成功跳转到 index.html 页面:如果 urls.py 文件中的 index 路径有所变化,views.py 文件中不需要更改index路径。
# urls.py文件
from django.urls import path, re_path
from app01 import views
urlpatterns = [
# 路径 login/的别名为login_page
re_path(r'^login/', views.login, name='login_page'),
# 路径 index/的别名为index_page
re_path(r'^index/', views.index, name='index_page'),
]
# ------------------------------------------------------------------
# views.py文件
from django.shortcuts import render, reverse, redirect, HttpResponse
def login(request):
# 当为GET请求时就返回登录页面
if request.method == 'GET':
return render(request, 'login.html')
else:
# post 请求时,就提取出请求数据。
user = request.POST.get('username')
pwd = request.POST.get('password')
if user == 'xiaoyang' and pwd == '123':
# 会对别名反向解析成路径/index/
url = reverse('index_page')
# 然后哦重定向到别名解析的路径
return redirect(url)
else:
return HttpResponse('登录失败')
def index(request):
return HttpResponse('登录成功!!!')
登录成功 HTML页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>登录页面</h1>
<!-- 这里的 "{% url 'login_page' %}" 相当于路径/login/-->
<form action="{% url 'login_page' %}" method="post">
{% csrf_token %} <!-- post提交需要做CSRF验证 -->
用户名:<input type="text" name="username">
密码:<input type="password" name="password">
<input type="submit">
</form>
</body>
</html>
分组反向解析
如果路径存在分组的反向解析使用:
无名分组:reverse ( "路由别名", arges = ( 符合正则匹配的参数 ) )
有名分组:reverse ( "路由别名", arges = { "分组名":"符合正则匹配的参数" } )
# urls.py文件
from django.urls import path, re_path
from app01 import views
urlpatterns = [
# 无名分组
re_path(r'^books/(\d{2})/', views.books, name='books_page'),
# 有名分组
re_path(r'^years/(?P<year>\d{4})/', views.years, name='years_page'),
]
# ------------------------------------------------------------------
# views.py文件
from django.shortcuts import render, reverse, redirect, HttpResponse
# 有名分组的反向解析
def years(request, year):
url = reverse('years_page', kwargs={'year': year})
# HTML 文件中:{% url 'years_page' 'year'=1234 %}
return HttpResponse(url)
# 无名分组的反向解析
def books(request, ret):
url = reverse('books_page', args=(ret,))
# HTML 文件中:{% url 'books_page' 34 %}
return HttpResponse(url)
六、名称空间
Django项目里有多个app,当在不同的 app 目录下的 urls.py 文件中定义了相同的路由别名 name 时,那么在反向解析时则会出现覆盖。
例如:
不管输入:http://127.0.0.1:8000/app01/index/ 还是 http://127.0.0.1:8000/app02/index/ 都得到的是 /app02/index/ app02路径的别名覆盖了app01 路径的别名
app01下的 urls.py 文件和 views.py 文件
# urls.py文件
from django.urls import path, re_path
from app01 import views
urlpatterns = [
re_path(r'^index/', views.index, name='index_page'),
]
# ------------------------------------------------------------------
# views.py文件
from django.shortcuts import render, reverse, HttpResponse
def index(request):
url = reverse('index_page')
return HttpResponse(url)
app02下的 urls.py 文件和 views.py 文件
# urls.py文件
from django.urls import path, re_path
from app02 import views
urlpatterns = [
re_path(r'^index/', views.index, name='index_page'),
]
# ------------------------------------------------------------------
# views.py文件
from django.shortcuts import render, reverse, HttpResponse
def index(request):
url = reverse('index_page')
return HttpResponse(url)
项目名称目录下的 urls 文件里面,统一将路径分发给各个 app 目录。
# urls.py文件
from django.urls import path, re_path, include
# 总路由表
urlpatterns = [
re_path(r'^app01/', include('app01.urls')),
re_path(r'^app02/', include('app02.urls')),
]
对于这种问题的解决方法就是避免使用相同的别名,如果要使用相同的别名,那就需要将别名放到不同的名称空间中去,这样就避免了即使出现了重复,彼此也不会冲突。
解决方法:
格式:
# 视图中的名称空间的方向解析
url=reverse('名称空间的名字:待解析的别名')
# 模板中的名称空间的反向解析
<a href="{% url '名称空间的名字:待解析的别名'%}">小杨</a>
1、在 urls.py 路由分发时指定名称空间
项目名称目录下的 urls 文件里面,统一将路径分发给各个 app 目录。
# url.py文件
from django.urls import path, re_path, include
# 总路由表
urlpatterns = [
# 给include传递一个元组,第一个是路由分发的地址,第二个是我们自定义的名称空间名字
re_path(r'^app01/', include(('app01.urls', 'app01'))),
re_path(r'^app02/', include(('app02.urls', 'app02'))),
]
2、修改每个app下的view.py中视图函数,对不同名称空间的别名做反向解析
app01下的 urls.py 文件和 views.py 文件
# urls.py文件
from django.urls import path, re_path
from app01 import views
urlpatterns = [
re_path(r'^index/', views.index, name='index_page'),
]
# ------------------------------------------------------------------
# views.py文件
from django.shortcuts import render, HttpResponse, reverse
def index(request):
# 解析的是app01下的别名‘index_page’
url = reverse('app01:index_page')
return HttpResponse(url)
app02下的 urls.py 文件和 views.py 文件
# urls.py文件
from django.urls import path, re_path
from app02 import views
urlpatterns = [
re_path(r'^index', views.index, name='index_page'),
]
# ------------------------------------------------------------------
# views.py文件
from django.shortcuts import render, reverse, HttpResponse
def index(request):
# 解析的是app02下的别名‘index_page’
url = reverse('app02:index_page')
return HttpResponse(url)
本文来自博客园,作者:Mr-Yang`,转载请注明原文链接:https://www.cnblogs.com/XiaoYang-sir/articles/14864957.html