Django路由层
一、MVC和MTV框架
MVC
- M 代表模型(Model)
- V 代表视图(View)
- C 代表控制器(Controller)
Web服务器开发领域里著名的MVC模式,所谓MVC就是把Web应用分为模型(M),控制器(C)和视图(V)三层,他们之间以一种插件式的、松耦合的方式连接在一起,模型负责业务对象与数据库的映射(ORM),视图负责与用户的交互(页面),控制器接受用户的输入调用模型和视图完成用户的请求。
MTV
Django 的 MTV 模式本质上和MVC是一样的,也是为 56c 了各组件间保持松耦合关系,只是定义上有些许不同,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之后,用 ur 56c l 需要引用 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 wo ad8 rld') # 响应信息
三、分组
分组就是需要直接从路径中取出参数,这就用到了正则表达式的分组功能了。
分组分为两种:无名分组与有名分组
无名分组
无名分组按照位置传参,需要一 一对应。
捕获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 请求时,就提取出请求数据。 use ad0 r = 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. 1569 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)
- django框架--路由系统
- 第三百零四节,Django框架,urls.py模块,views.py模块,路由映射与路由分发以及逻辑处理——url控制器
- Django中的路由URLS
- Django视频教程第二课:应用与路由
- Django新手上路——路由匹配
- Django知识点-URL路由 name=
- Django 视图,模板,路由规则
- Django路由再议
- Python学习(三十一)—— Django之路由系统
- Django入门2:路由系统
- Django 路由系统(URLconf)
- Django路由系统
- Django_3_路由
- Django 之 URL(路由)分发机制
- django drf框架自带的路由及最简化的视图
- Django 路由
- Django新手上路——路由补充
- 20190319Django路由分配与模板渲染
- [Django框架之路由层匹配、有名 无名分组、反向解析、路由分发、名称空间、伪静态、本地虚拟环境、django版本区别]
- Django路由系统