路由
04 路由层
一. 路由匹配
1. 路由匹配注意事项
urlpatterns = [
url(r'^admin/', admin.site.urls),
# 首页
url(r'^$',views.home),
# 路由匹配
url(r'^test/$',views.test),
url(r'^testadd/$',views.testadd),
# 尾页(了解): 后期使用异常捕获处理, 这样的尾页让django的第二次在路径中斜杠APPEND_SLASH=True失去了意义, 第二次的重定向追加斜杠的机制等于失效了.
url(r'',views.error),
]
'''
1. url方法第一个参数是正则表达式, 只要第一个参数正则表达式能够匹配到内容 那么就会立刻停止往下匹配, 而是直接调用对应的视图函数
2. 不需要添加一个前导的反斜杠,因为每个URL 都有。例如,应该是^test 而不是 ^/test。
3. 每个正则表达式前面的'r' 是可选的但是建议加上。r是raw的意思标识原生字符串,声明被修饰的字符串中所有字符是普通字符。
4. 在第一次正则匹配机制没有匹配到想要的内容时,会在匹配的内容后面加一个斜杠\, 然后django内部帮你进行重定向在重头开始再匹配一次
5. 每个由正则匹配成功后所捕获的参数都作为一个普通的Python字符串传递给视图
'''
2. 取消路由匹配第一次匹配不成功,在匹配的内容后面加一个斜杠\,重定向重新匹配
# 取消自动加斜杠: 到settings.py文件中写入以下内容
APPEND_SLASH = False # 默认是True自动加斜杠
二. 分组命名匹配
1. 无名分组
# 无名分组: 无须定义名字
路由书写: url('^index/(\d+)', views.index)
内部逻辑: 括号内正则表达式匹配到的内容将会当作位置参数传递给与之绑定的视图函数index
视图函数:
def index(request,xx):
print(xx)
return HttpResponse('index')
2. 有名分组
# 有名分组: 需定义名字
路由书写: url('^index/(?P<year>\d+)', views.index)
内部逻辑: 括号内正则表达式匹配到的内容将会当作关键字参数传递给与之绑定的视图函数index
视图函数:
def index(request,year):
print(year)
return HttpResponse('index')
3. 无名有名是否可以混合使用
# 又名无名分组不可混用, 不过可以单种多次使用.
url('^index/(\d+)/(\d+)/', views.index)
url('^index/(?P<year>\d+)/(?P<year>\d+)/', views.index)
# 多次使用如果是无名分组, 视图函数中可以使用*args接收参数
def index(request, *args):
print(args)
return HttpResponse('index')
# 多次使用如果是有名分组, 视图函数中可以使用**kwargs接收参数
def index(request, **kwargs):
print(kwargs)
return HttpResponse('index')
三. 传递额外的参数给视图函数(了解)
URLconfs 具有一个钩子,让你传递一个Python 字典作为额外的参数传递给视图函数。
django.conf.urls.url()
函数可以接收一个可选的第三个参数,它是一个字典,表示想要传递给视图函数的额外关键字参数。
例如:
from django.conf.urls import url
from . import views
urlpatterns = [
url(r'^blog/(?P<year>[0-9]{4})/$', views.year_archive, {'foo': 'bar'}),
]
在这个例子中,对于/blog/2005/请求,Django 将调用views.year_archive(request, year='2005', foo='bar')。
这个技术在Syndication 框架中使用,来传递元数据和选项给视图。
四. 反向解析
反向解析也叫反向URL 匹配、反向URL 查询或者简单的URL 反查。
1. 什么是反向解析?
反向解析就是通过一些方法得到一个结果 该结果可以直接访问对应的url触发视图函数
2. 反向解析基本使用
第一步: 在urls.py中给路由与视图函数取一个别名(注意: 别名不能重复, 一定要唯一)
url('^index/', views.index, name='xxx')
第二步: 使用反向解析
在模板层使用:
{% url 'xxx' %}
<a href="{% url 'ooo' %}">111</a>
在视图层使用:
from django.shortcuts import reverse
reverse('xxx')
3. 无名有名分组反向解析
3.1 无名分组反向解析
# 路由层配置无名分组反向解析
url(r'^index/(\d+)/',views.index,name='xxx')
# 模板层使用反向解析
{% url 'xxx' 123 %}
# 视图层使用反向解析
reverse('xxx', args=(1, ))
"""
路由层url分组匹配机制这个数字并不是写死的,
一般情况下放的是数据的主键值, 我们可以通过获取到的数据的主键值进而定位到数据对象,
从而可以对数据进行编辑和删除, 如下示例:
"""
# 路由层配置无名分组反向解析
url(r'^edit/(\d+)/', views.edit, name='xxx')
# 视图层使用反向解析
def edit(request,edit_id):
reverse('xxx', args=(edit_id,))
# 模板层使用反向解析
{% for user_obj in user_queryset %}
<a href="{% url 'xxx' user_obj.id %}">编辑</a>
{% endfor %}
3.2 有名分组反向解析
# 有名分组反向解析
url(r'^func/(?P<year>\d+)/',views.func,name='ooo')
# 模板层使用反向解析
第一种写法:
<a href="{% url 'ooo' year=123 %}">111</a>
第二种写法: 推荐
<a href="{% url 'ooo' 123 %}">222</a>
# 视图层使用反向解析
第一种写法:
print(reverse('ooo',kwargs={'year':123}))
第二种写法:
print(reverse('ooo',args=(111,)))
四. 路由分发
django
的每一个应用都可以有自己的templates
文件夹, urls.py
文件, 以及static
文件夹, 只有基于上述的这种特点, django
就能够非常好的做到分组开发, 也就是说每个人只写自己的app
中的功能就可以了.
因此作为组长 只需要将手下书写的app
全部拷贝到一个新的django
项目中 然后在配置文件里面注册所有的app再利用路由分发的特点将所有的app整合起来就可以了.
因此, 当一个django
项目中的url特别多的时候, 总路由urls.py
代码非常冗余不好维护这个时候也可以利用路由分发来减轻总路由的压力.
利用路由分发之后, 总路由不再干路由与视图函数的直接对应关系,而是做一个分发处理, 进而识别当前url
所属的应用, 最后直接分发给对应的应用去处理就行了.
总路由:
Copyfrom django.conf.urls import url
from django.conf.urls import include
from django.contrib import admin
urlpatterns = [
url(r'^admin/', admin.site.urls),
# 方式一:
from app01 import urls as app01_urls
from app02 import urls as app02_urls
url(r'^app01/', include(app01_urls)) # 只要url前缀是app01开头 全部交给app01处理
url(r'^app02/', include(app02_urls))
# 方式二: 终极写法 推荐
url(r'^app02/', include('app01.urls'))
url(r'^app01/', include('app02.urls'))
]
# 注意: 总路由中不要使用`^app01/$`这种格式, 不然无法分发给子路由
子路由设置:
# app01中urls.py的配置
from django.conf.urls import url
from app01 import views
urlpatterns = [
url(r'^index/', views.index),
]
# app02中urls.py的配置
from django.conf.urls import url
from app02 import views
urlpatterns = [
url(r'^index/', views.index),
]
五. 名称空间
为什么需要名称空间?
"""
当多个应用设置了相同的别名, 使用反向解析时, 如果经过路由匹配规则匹配之后结果一样,
就会出现后面的覆盖前面的, 如果你是想触发的是前面的视图函数, 那么将访问不到.
正常情况下的反向解析是没有办法自动识别前缀的.
注意!!!: 这里说的是正常情况下, 如果反向解析所对应的url分组个数一样, 那么是可以识别的.
"""
总路由中配置名称空间:
from django.conf.urls import url
from django.conf.urls import include
urlpatterns = [
url('^app01/', include('app01.urls', namespace='app01')),
url('^app02/', include('app02.urls', namespace='app02')),
]
子路由中匹配规则与别名相同:
# app01中urls.py的配置
from django.conf.urls import url
from app01 import views
urlpatterns = [
url('^reg/', views.reg, name='reg'),
]
# app02中urls.py的配置
from django.conf.urls import url
from app02 import views
urlpatterns = [
url('^reg/', views.reg, name='reg'),
]
子路使用反向解析:
# 视图层使用名称空间语法进行反向解析
reverse('app01:reg')
reverse('app02:reg')
# 模板层使用名称空间语法进行反向解析
{% url 'app01:reg' %}
{% url 'app02:reg' %}
补充: 其实只要保证名字不冲突 就没有必要使用名称空间
"""
一般情况下 有多个app的时候我们在起别名的时候会加上app的前缀
这样的话就能够确保多个app之间名字不冲突的问题
"""
urlpatterns = [
url(r'^reg/',views.reg,name='app01_reg')
]
urlpatterns = [
url(r'^reg/',views.reg,name='app02_reg')
]
六. 伪静态
# 什么是静态网页?
静态网页数据是写死的, 万年不变的.
# 什么是伪静态?
伪静态是将一个动态网页伪装成静态网页.
# 为什么要有伪静态呢?
https://www.cnblogs.com/Dominic-Ji/p/9234099.html
伪静态的目的在于增大本网站的SEO查询力度, 并且增加搜索引擎收藏本网上的概率.
(SEO全称Search Engine Optimization 搜索引擎优化)
其实, 搜索引擎本质上就是一个巨大的爬虫程序,
总结:
无论你怎么优化 怎么处理
始终还是干不过人命币玩家
路由层使用伪静态: reg.html
# 路由配置
urlpatterns = [
url(r'^register.html', views.register),
]
# 浏览器url访问
http://127.0.0.1:8002/app01/register.html/
七. 虚拟环境
[
在正常开发中 我们会给每一个项目配备一个该项目独有的解释器环境
该环境内只有该项目用到的模块 用不到一概不装
linux: 缺什么才装什么
虚拟环境
你每创建一个虚拟环境就类似于重新下载了一个纯净的python解释器
但是虚拟环境不要创建太多,是需要消耗硬盘空间的
扩展:
每一个项目都需要用到很多模块 并且每个模块版本可能还不一样
那我该如何安装呢? 一个个看一个个装???
开发当中我们会给每一个项目配备一个requirements.txt文件
里面书写了该项目所有的模块即版本
你只需要直接输入一条命令即可一键安装所有模块即版本
八. django版本区别
1. django1.X路由层使用的是url方法, 而在django2.Xhe3.X版本中路由层使用的是path方法
url() 第一个参数支持正则
path()第一个参数是不支持正则的 写什么就匹配什么
如果你习惯使用path那么也给你提供了另外一个方法
from django.urls import path, re_path
from django.conf.urls import url
re_path(r'^index/',index),
url(r'^login/',login)
提示: 2.X和3.X里面的re_path就等价于1.X里面的url
2. 虽然path不支持正则 但是它的内部支持五种转换器
path('index/<int:id>/',index)
# 将<int:id>中匹配到的内容, 先转成int类型, 再以关键字参数传递给与之绑定的index视图函数
def index(request,id):
print(id,type(id))
return HttpResponse('index')
str,匹配除了路径分隔符(/)之外的非空字符串,这是默认的形式
int,匹配正整数,包含0。
slug,匹配字母、数字以及横杠、下划线组成的字符串。
uuid,匹配格式化的uuid,如 075194d3-6885-417e-a8a8-6c931e272f00。
path,匹配任何非空字符串,包含了路径分隔符(/)(不能用?)
3. 除了有默认的五个转换器之外 还支持自定义转换器(了解)
class MyConverter:
regex='\d{2}' # 属性名必须为regex
def to_python(self, value):
return int(value)
def to_url(self, value):
return value # 匹配的regex是两个数字,返回的结果也必须是两个数字
from django.urls import path,register_converter
from app01.path_converts import MonthConverter
# 先注册转换器
register_converter(MonthConverter,'mon')
from app01 import views
urlpatterns = [
path('articles/<int:year>/<mon:month>/<slug:other>/', views.article_detail, name='aaa'),
]
4. 模型层里面1.X外键默认都是级联更新删除的, 但是到了2.X和3.X中需要你自己手动配置参数
Copymodels.ForeignKey(to='Publish')
models.ForeignKey(on_delete=models.CASCADE, on_update=models.CASCADE)
九. 总结
# 路由匹配
1. urls.py中url方法第一个参数是正则
2. Django默认帮你开启了第一次匹配不成功, 往匹配效验的路径末尾追加一个斜杠/, 再进行重定向重新遍历循环匹配一次
配置: 默认settings.py中没有需要自己添加
APPEND_SLASH = False/True
3. 定义首页url匹配规则: url('^$', views.home)
4. 定义尾页url匹配规则: url('', views.error)
提示: 尾页一般不使用这种方式, 这种方式会让jango的默认APPEND_SLASH机制失效
之后我们可以使用异常处理, 来作为尾页内容.
# 分组
# 无名分组: 无须定义名字
路由书写: url('^index/(\d+)', views.index)
内部逻辑: 括号内正则表达式匹配到的内容将会当作位置参数传递给与之绑定的视图函数index
视图函数:
def index(request, xxx):
pass
# 有名分组: 需定义名字
路由书写: url('^index/(?P<year>\d+)', views.index)
内部逻辑: 括号内正则表达式匹配到的内容将会当作关键字参数传递给与之绑定的视图函数index
视图函数:
def index(request, year):
pass
# 注意事项:
又名无名分组不可混用, 不过可以单种多次使用.
url('^index/(\d+)/(\d+)/', views.index)
url('^index/(?P<year>\d+)/(?P<year>\d+)/', views.index)
多次使用如果是无名分组, 视图函数中可以使用*args接收参数
def index(request, *args):
pass
多次使用如果是有名分组, 视图函数中可以使用**kwargs接收参数
def index(request, *kwargs):
pass
# 反向解析
# 什么是反向解析?
反向解析就是通过一些方法得到一个结果 该结果可以直接访问对应的url触发视图函数
# 反向解析使用
第一步: 在urls.py中给路由与视图函数取一个别名(注意: 别名不能重复, 一定要唯一)
url('^index/', views.index, name='xxx')
第二部: 使用反向解析
在模板层使用:
{% url 'xxx' %}
<a href="{% url 'ooo' %}">111</a>
在视图层使用:
from django.shortcuts import reverse
reverse('xxx')
# 无名有名分组反向解析
# 无名分组反向解析
# 路由层定义
url(r'^index/(\d+), views.index, name='index')
# 模板层使用:
{% url 'index' 数字 %}
# 视图层使用:
from django.shortcuts import reverse
def index(request, xxx)
reverse('index', agrs=(xxx, )
# 有名分组反向解析
# 路由层定义
url(r'^index/(?P<year>\d+), views.index, name='index')
# 模板层使用:
{% url 'index' year=数字 %}
{% url 'index' 数字 %}
# 视图层使用:
from django.shortcuts import reverse
def index(request, year)
reverse('index', kwagrs=({'year': 数字})
reverse('index', agrs=(数字, )
# 无名有名分组反向解析使用场景
应用: 一般用作主键值
模板层应用: 通过给HTMl页面绑定分组反向解析, 等待用户提交时, 同时携带用户数据的主键值,
视图层获取到不同操作反馈过来的动态主键值, models就可以基于这个主键值, 来对当前操作
用户的其他数据进行定位.
视图层应用: 通过反向解析进行重定向操作
# 注意: 无名有名分组反向解析是争对单个情况的解析, 对于反向解析时的参数,
只要是满足对于路由的正则匹配规则就行.
# 路由分发:
# 因该分发的部分: templates, urls.py, models.py
# 路由分发的优势: 多应用之间路由不冲突, 减轻总路由的压力及维护性差的问题, 解耦合可以更好的实现分组开发
# 总路由设置:
from django.conf.urls import url
from django.conf.urls import include
urlpatterns = [
url(r'^admin/', admin.site.urls),
# 方式一:
from app01 import urls as app01_urls
from app02 import urls as app02_urls
url(r'^app01/', include(app01_urls)) # 只要url前缀是app01开头 全部交给app01处理
url(r'^app02/', include(app02_urls))
# 方式二: 终极写法 推荐
url(r'^app02/', include('app01.urls'))
url(r'^app01/', include('app02.urls'))
]
# 注意: 总路由中不要使用`^app01/$`这种格式, 不然无法分发给子路由
# 子路由设置:
# app01中urls.py的配置
from django.conf.urls import url
from app01 import views
urlpatterns = [
url(r'^index/', views.index),
]
# app02中urls.py的配置
from django.conf.urls import url
from app02 import views
urlpatterns = [
url(r'^index/', views.index),
]
# 名称空间
# 为什么需要名称空间?
当多个应用中的路由设置了相同的别名, 使用反向解析时, 就会出现后面的覆盖前面的, 如果你是想触发的是前面的视图函数, 那么将访问不到.
# 总路由中配置名称空间
from django.conf.urls import url
from django.conf.urls import include
urlpatterns = [
url('^app01/', include('app01.urls', namespace='app01')),
url('^app02/', include('app02.urls', namespace='app02')),
]
# 子路由中匹配规则与别名相同
# app01中urls.py的配置
from django.conf.urls import url
from app01 import views
urlpatterns = [
url('^reg/', views.reg, name='reg'),
]
# app02中urls.py的配置
from django.conf.urls import url
from app02 import views
urlpatterns = [
url('^reg/', views.reg, name='reg'),
]
# 子路由使用名称空间语法进行反向解析
# 视图层使用名称空间语法进行反向解析
reverse('app01:reg')
reverse('app02:reg')
# 模板层使用名称空间语法进行反向解析
{% url 'app01:reg' %}
{% url 'app02:reg' %}
# 补充: 其实只要保证名字不冲突 就没有必要使用名称空间
urlpatterns = [
url('^reg/', views.reg, name='app01_reg'),
]
urlpatterns = [
url('^reg/', views.reg, name='app02_reg'),
]
# 伪静态
静态网页: 网页数据写死
伪静态: 将动态网页伪装成静态网页
作用: 提升网站SEO查询力度, 增加收藏网页概率. -> 爬虫搜索引擎
路由层使用伪静态:
urlpatterns = [
url('^reg.html/', views.reg, name='app01_reg'),
]
# 虚拟环境部署
# django版本区别
1. django1.x版本路由层使用的是url方法, 而django2.x和3.x路由层使用的是path方法
1.x版本路由匹配支持正则
2.x和3.x版本path不支持正则, 如果想要支持正则导入re_path. 虽然保留了url方法但是不推荐使用了.
2. django2.x和3.x版本虽然不支持正则, 但是它内部支持五种装换器
from django.urls import path
path('index/<int:id>', views.index)
将<int:id>中匹配到的内容, 先转成int类型, 再以关键字参数传递给与之绑定的index视图函数
3. 转换器支持自定义
4. 模型层中1.x默认级联更新级联删除, 而2.x和3.x版本需要手动指定
publish = models.ForeignKey(to='Publish', on_update=models.CASCADE, on_delete=models.CASCADE)
十. 补充: url中的第三个参数{}
def url(regex, view, kwargs=None, name=None)
...
# urls.py
url(r'^third_params/', views.third_params, {'username': 'egon'}, name='third_params'),
# views.py
# def third_params(request, path): # 注意: 关键字形式传参
def third_params(request, username):
return HttpResponse(username) # username返回到前端页面就是egon