路由层
一.路由匹配
1.路由匹配注意事项
urls.py文件
from django.conf.urls import url
from django.contrib import admin
from route_app import views
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),
]
注意事项:
-
url方法的第一个参数为正则表达式,只要有一个参数正则表达式能够匹配到内容,那么久会立刻停止往下匹配,并且立马调用对应的视图函数
-
不需要添加一个前导的反斜杠,因为每个url 都有.
例如: 应该是test,而不是/test.
-
每个正则表达式前面的r是可选的,代表里面的字符为原生字符,r为raw即标识原生字符串.
-
在第一次正则机制没有匹配到想要的内容时,会在url后面加一个,django内部会帮你再加上\后重定向重新再匹配一次.
-
每个由正则匹配成功后,所捕获的参数都作为一个普通的python字符串传递给视图.
2.取消路由匹配第一次不成功后,加上\后缀重新匹配一次
# 取消自动加上\重定向匹配一次 进入settings.py内 写入下方代码
APPEND_SLASH = False # 这个变量默认为True
二.分组命名匹配
当我们在正则表达式中进行了分组,即加上了括号之后,该括号内匹配上的内容会当做参数传给视图,视图必须得接受这个参数,不然就会报错.
无名分组
无需定义名字的分组
-- 路由书写: url('^index/(\d+)', views.index)
-- 内部逻辑: 括号内正则表达式匹配到的内容将会当做位置参数传递给对应的视图函数index
-- 视图函数:
def index(request,a): # 必须要有a这个参数,当然也可以是别的名字
print(a)
return HttpResponse("index")
有名分组
需要定义名字的分组
给分组取别名: (?P<别名>)
-- 路由书写: url('^index/(?P<id>\d+)', views.index)
-- 内部逻辑: 括号内正则表达式匹配到的内容将会当做关键字参数传递给对应的视图函数index
-- 视图函数:
def index(request,id) # 必须要有id这个参数,名字也得叫id,
print(id)
return HttpResponse("index")
混搭?
注意: 有名分组是不能和无名分组一同使用的.
但是可以有名和有名,无名和无名搭配
即代码演示
# 先来个无名和有名的搭配
url("^index/(\d+)/(?P<id>d+)", views.index)
def index(request,a,id):
print(a,b)
return HttpResponse("会报错!!!")
# 浏览器会报错,它会说收不到你传入的参数a,why? 没有为什么就是不能混搭,即便你遵循函数的参数规则
# 但是无名和无名已经有名和有名之间还是可以多次重复的
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 app import views
urlpatterns = [
url(r"^blog/(?P<year>[0-9]{4})/$", views.year_archive, {"foo": "bar"}) # 这第三个参数就是传入给视图函数year_archive的额外的关键字参数
]
在这个例子中,对于/blog/2005/请求,Django 将调用 views.year_archive(request, year="2005", foo="bar")
这个技术在Syndication 框架中使用, 用来传递元数据和选项给视图
四.反向解析
反向解析也叫反向url匹配, 反向url查询或者简单的url反查
1.什么是反向解析
反向解析就是通过一些方法得到一个结果, 改结果可以直接访问对应的url触发视图函数
2.反向解析的基本使用
第一步: 在urls.py 中 给路由与视图函数取一个别名(注意: 别名不能重复,一定要唯一)
url(r"^index/", views.index, name="xxx")
第二步: 使用反向解析
- 前端: 在模板层使用
{% url "xxx" %}
<a href="{% url 'yyy' %}">111</a>
- 后端: 在视图层使用
- 第一种:
from django.shortcuts import reverse
reverse("xxx")
- 第二种
from django.shortcuts import redirect
redirect("{% url xxx %}")
- 第一种:
3.无名有名分组反向解析
3.1 无名分组反向解析
路由层 匹配无名分组反向解析
url(r'^index/(\d+)/', views.index,name="xxx")
模板层使用反向解析
{% url 'xxx' 123 %}
- 注意这个123必须要有,不然就会报错,它不知道你的
\d+
该解析成撒
视图层使用反向解析
reverse("xxx", args=(1,))
- 注意这个
args=(1,)
也是同理
路由层url分组匹配机制这个数字并不是写死的.
一般情况下放得水数据的主键值,我们可以通过获取到的数据的主键值进而定位到数据对象,从而可以对数据进行编辑和删除
如下:
-- urls.py
url(r'^edit/(\d+)/', views.edit, name='xxx')
-- views.py
def edit(request,edit_id):
reverse("xxx", args=(edit_id,))
-- tmplates模板下的html
{% for user_info in user_queryset %}
<a href="{% url "xxx" user_info.id %}"> 编辑 </a>
{% endfor %}
3.2 有名分组反向解析
-- 有名分组反向解析
url(r'^func/(?P<id>\d+/), views.func, name="yyy"')
-- 模板层使用反向解析
第一种写法:
<a href="{% url 'yyy' id=11 %}">1111</a>
第二种写法:
<a fref="{% url 'yyy' 11 %}">2222</a>
-- 视图层使用反向解析
第一种写法:
print(reverse('yyy', kwargs={"id":121}))
第二种写法:
print(reverse('yyy', args=(121,)))
五.路由分发
"""
django的每一个应用都可以有自己的templates文件夹, urls.py文件, 以及static文件夹, 只有基于上述的这种特点, django就能够非常好的做到分组开发, 也就是说每个人只写自己的app中的功能就可以了.
因此作为组长 只需要将手下书写的app全部拷贝到一个新的django项目中 然后在配置文件里面注册所有的app再利用路由分发的特点将所有的app整合起来就可以了.
因此, 当一个django项目中的url特别多的时候, 总路由urls.py代码非常冗余不好维护这个时候也可以利用路由分发来减轻总路由的压力.
利用路由分发之后, 总路由不再干路由与视图函数的直接对应关系,而是做一个分发处理, 进而识别当前url所属的应用, 最后直接分发给对应的应用去处理就行了.
"""
总路由:
urls.py
from django.conf.urls import url
from djangoc.cong.urls import include
from django.contrib import admin
urlpatterns = [
url(r"^admin/",admin.site.urls),
# 方式1: 通过导入对应的app下的urls文件
from app01 import urls as app01_urls
from app02 import urls as app02_urls
url(r"^app01/",include(app01_urls)),
url(r"^app02/",include(app02_urls)),
# 方式2: 通过直接在字符串中点的方式
url(r"^app01/",include("app01.rls")),
url(r"^app02/",include("app02.rls")),
]
子路由配置:
# 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(r"^app01/", include('app01.urls'), namespace="app01"),
url(r"^app02/", include('app02.urls', namespace="app02")),
]
子路由中匹配规则与别名相同:
# app01 中的urls.py的配置
from django.conf.urls import url
from app01 import views
urlpatterns = [
url(r"^index/",views.index,name="index_name"),
]
# app02 中的urls.py的配置
from django.conf.urls import url
from app02 import views
urlpatterns = [
url(r"^index/",views.index,name="index_name"),
]
子路由使用反向解析:
# 视图层使用名称空间语法进行反向解析
reverse('app01:reg')
reverse('app02:reg')
# 模板层使用名称空间语法进行反向解析
{% url 'app01:reg' %}
{% url 'app02:reg' %}
子路由使用反向解析:
# 视图层使用名称空间语法进行反向解析
reverse('app01.index_name')
reverse('app02.index_name')
# 模板层使用名称空间语法进行反向解析
{% url 'app01.index_name' %}
{% url 'app02.index_name' %}
方式2: 其实保证名字不冲突,就没必要使用名称空间
一般情况下, 有多个app的时候,我们在起别名的时候,会加上app的前缀,这样的话就可以保证每个app之间名字不会冲突的问题
例:
#app01.urls.py
urlpatterns = [
url(r'^reg/',views.reg,name='app01_reg')
]
# app02.urls.py
urlpatterns = [
url(r'^reg/',views.reg,name='app02_reg')
]
七.伪静态
1.什么是静态网页?
- 静态网页数据是写死的,万年不变的.
2.什么是伪静态?
-
伪静态是将一个动态网页伪装成一个静态网页,一般会在url的后面加上.html的字样.
-
例如:
3.为什么要有伪静态?
- 伪静态的目的在于增大网站的seo查询力度,并且增加搜索引擎收藏本网页的概率.
- seo全称search engine optimization 搜索引擎优化
其实搜索引擎本质上就是一个巨大的爬虫程序.
总结:
- 无论你再怎么优化 再怎么处理
- 始终干不过rmb玩家.百度的搜索引擎下方的头几条都是广告
路由层使用伪静态: index.html
-- app01.urls.py
urlpatterns = [
url(r"index.html/", views.index)
]
# 浏览器*问就要加上html的后缀了
http://127.0.0.1:8002/app01/index.html/
八.本地虚拟环境
在本地创建一个虚拟环境
会有一个创建的过程
查看初始的环境变量的配置
介绍:
在正常开发中 我们会给每一个项目配备一个该项目独有的解释器环境
该环境内只有该项目用到的模块 用不到一概不装
linux: 缺什么才装什么
虚拟环境
你每创建一个虚拟环境就类似于重新下载了一个纯净的python解释器
但是虚拟环境不要创建太多,是需要消耗硬盘空间的
扩展:
每一个项目都需要用到很多模块 并且每个模块版本可能还不一样
那我该如何安装呢? 一个个看一个个装???
开发当中我们会给每一个项目配备一个requirements.txt文件
里面书写了该项目所有的模块即版本
你只需要直接输入一条命令即可一键安装所有模块即版本
拓展: requirements.txt 的 一键安装对应的模块和版本
python
语言算是比较早提出包管理概念的, 使用pip
安装依赖的确是非常方便. 对于一些简单的脚本或爬虫, 我们一般直接使用系统python环境安装相关依赖. 稍微大一点的项目, 比如使用了Django
, 那么所需要的依赖就非常多, 这个时候使用venv
隔离环境就非常好. 但我们的问题是, 当我们将项目移到另一个环境中时, 这些依赖怎么安装, 还是一个一个对比执行? 这个时候我们可能会怀念java
的pom.xml
, php
的composer.json
, nodejs
的package.json
, 当然python
也有自己的解决方法.
1. 导出原项目的依赖
pip freeze > requirements.txt
导出文件的格式大概是这样的
2. 在新项目中一次性安装依赖
pip install -r requirements.txt
九.django版本的区别
1.django1.X路由层使用的是url方法,而2.X和3.X版本路由层中使用的path方法
-- url() 第一个参数为一个正则表达式
-- path() 第一个参数不支持正则表达式, 写什么就是什么
当然如果你习惯了写url方法,那么2.x和3.x也给你提供了其他的方法.
from django.urls import path,re_path
from django.conf.urls import url
re_path(r"^index", index)
url(r'^login', login)
ps: 2.x和3.x中的re_path()方法就等价与1.x的url()方法
2.虽然path不支持正则 但是它的内部支持转换器
这里介绍五大转换器
str
,匹配除了路径分隔符(/)之外的非空字符串,这是默认的形式.
int
,匹配正整数,包含0.
slug
,匹配字母,数字以及下划线,横杆组成的字符串.
uuid
,匹配格式化的uuid,如 075194d3-6885-417e-a8a8-6c931e272f00。
path
,匹配任何非空字符串,包含了路径分隔符(/)不能用?
使用
urls.py
# 将<int:id>中匹配到的内容,先转成int类型,再以关键字参数传递给与之绑定的index视图函数
path('index/<int:id>/',index)
views.py
def index(request,id):
print(id,type(id))
return HttpResponse('index')
3.除了有默认的五个转换器之外, 还支持自定义的转换器(了解)
在app01下创建一个新的文件 path_converts.py
该文件夹内容为自定义的转换器
class MonthConverter:
regex='\d{2}' # 属性名必须为regex
def to_python(self, value):
return int(value)
def to_url(self, value):
return value # 匹配的regex是两个数字,返回的结果也必须是两个数字
在app01.urls.py
from app01 import views
urlpatterns = [
path('articles/<int:year>/<mon:month>/<slug:other>/', views.article_detail, name='aaa'),
]
在app01.views.py中
from django.urls import path,register_converter
from app01.path_converts import MonthConverter
# 先注册转换器
register_converter(MonthConverter,'mon')
4.模型层里面1.x外键默认都是级联更新删除的,但是到了2.x和3.x中需要你自己动手配置参数
models.ForeignKey(to="Publish")
models.ForeignKey(on_delete=models.CASCADE, on_update=models.CASCEAD)
十.总结
-- 路由匹配
-- 1. urls中url方法第一个参数是正则
-- 2. 当你的url第一次匹配不成功的时候,django默认会给你的url最后加一个斜杆`/`,然后会重定向在路由列表中重新匹配一次.
-- 当然如果你想取消掉这重定向的匹配的过程,即可到settings.py中加上一句.
APPEND_SLASH = False # 默认是True
-- 3. 定义首页url匹配规则: url(r"^$", views.home)
-- 4. 定义尾部url匹配规则: url(r"", views.error)
-- ps: 尾部一般不使用这种方法,这种方法会让django的默认APPEND_SLASH机制失效
-- 分组
-- 无名分组: 无需定义名字
-- 路由书写: url(r"^index/(\d+)", views.index)
-- 内部逻辑: 括号内正则表达式匹配到的内容将会当做位置参数传递给与之绑定的视图函数index
-- 视图函数:
def index(request, xx)
return HttpResponse('xx为无名分组的内容')
-- 有名分组: 需要其别名的分组
-- 路由书写: url(r"^index/(?P<id>\d+)", views.index)
-- 内部逻辑: 括号内正则表达式匹配到的内容将会当做关键字参数传递给与之绑定的视图函数index
-- 视图函数:
def index(request, id)
return HttpResponse('id为有名分组的内容'
-- 注意事项
-- 有名不能和无名分组混搭,但是可以有名和有名,无名和无名多次混搭.
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(r"^index/", views.index,name='index_name')
-- 第二步: 使用反向解析
-- 在模板层使用
{% url 'index_name' %}
<a href="{% url 'index_name' %}">1111</a>
-- 在视图层使用
from django.shortcuts import reverse
reverse('index_name')
-- 无名有名分组反向解析
-- 无名分组反向解析
-- 路由层定义
url(r"^index/(\d+)", views.index,name="inde_name")
-- 模板层使用
{% url 'index_name' 数字 %}
-- 视图层使用
from django.shortcuts import reverse
def index(request, xxx):
reverse('index_name', args=(xxx,))
-- 有名分组反向解析
--路由层定义
url(r'^index/(?P<id>\d+), views.index, name='index_name")
-- 模板层使用:
{% url 'index_name' id=数字 %}
{% url 'index_name' 数字 %}
-- 视图层使用
from django.shortcuts import reverse
def index(request,id):
reverse('index_name', kwargs={'id':数字})
reverse('index_name', args=(数字,))
-- 无名有名分组反向解析使用场景
应用: 一般用作主键值
模板层应用: 通过给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': 'jkey'}, name='third_params'),
# views.py
# def third_params(request, path): # 注意: 关键字形式传参
def third_params(request, username):
return HttpResponse(username) # username返回到前端页面就是jkey