django后端之路由层(url)
数据增删改查
针对数据对象主键字段的获取可以使用更加方便的 obj.pk获取,在模型类中定义双下str方法可以在数据对象被执行打印操作的时候方便的查看。
form表单中能够触发调剂动作只要两个
<input type='submit'/>
<button></button>
总结:
1.request对象方法
request.GET\request.POST\request.method
2.数据库迁移命令
makemigrations\migrate
3.form表单提交数据的特征
action参数:三种情况\method参数:两种情况
步骤:
1.数据展示功能
开设接口、获取数据、传递页面、展示数据
2.数添加功能
开设接口、获取数据、发送数据、校验数据、录入数据、重定向
3.数据编辑功能
开设接口、后端如何区分所要编辑的数据(问号携带参数)、后端获取用户数据、前端展示默认数据、获取用户并完成更新
4.数据删除功能
开设接口、问号携带参数、删除二次确认
代码展示:数据库使用的是django自身自带的sqlite3
models.py
class User(models.Model):
# ORM不用写主键字段,会自动创建主键字段id
# id = models.AutoField(primary_key=True)
name = models.CharField(max_length=32,verbose_name='用户名')
age = models.IntegerField(verbose_name='年纪')
def __str__(self):
return '用户对象:%s' %self.name
#便于对象打印之后的查看,不影响数据库 所有不需要执行迁移命令
urls.py
urlpatterns = [
path('admin/', admin.site.urls),
# 访问用户数据的接口
path('user_list/',views.user_list_func),
# 添加用户数据的接口
path('user_add/',views.user_add_func),
# 编辑用户数据的接口
path('user_edt/',views.user_editor_func),
# 删除用户数据的接口
path('user_delete/',views.user_delete_func)
]
views.py
from django.shortcuts import render,HttpResponse,redirect
from app01 import models
# Create your views here.
def user_list_func(request):
# 1.获得user表中所有的数据展示到html页面上
user_date =models.User.objects.filter() # 括号里不写等于查看所有[对象1,对象2]
# print(user_date)
# 2.利用模板语法传递数据到页面并完成处理最终返回html页面
return render(request,'userlistPage.html',{'user_data':user_date})
def user_add_func(request):
#2.根据不同的请求方式做不同的处理
if request.method=='POST':
#3.获取用户相关数据
name_data = request.POST.get('name')
age_data = request.POST.get('age')
if len(name_data)==0 or len(age_data)==0:
return HttpResponse('用户名和年龄不能为空')
user_data =models.User.objects.filter(name=name_data)
if user_data:
return HttpResponse('用户名已经存在')
models.User.objects.create(name=name_data,age=age_data)
#5,重定向到数据展示页
return redirect('/user_list/')
# 1.先返回一个数据新增数据的html页面
return render(request,'userAddPage.html')
def user_editor_func(request):
#1.获取用户想要编辑的数据主键值
target_edit_id = request.GET.get('edit_id')
#4.根据不同的请求处理不同的逻辑
if request.method=='POST':
name_data = request.POST.get('name')
age_data=request.POST.get('age')
if len(name_data)==0 or len(age_data)==0:
return HttpResponse('用户名和年龄不能为空')
models.User.objects.filter(pk=target_edit_id).update(name=name_data,age=age_data)
#5,重定向到数据展示页
return redirect('/user_list/')
#2 根据主键值获取对应的数据
target_edit_obj = models.User.objects.filter(pk=target_edit_id)[0]
# print(target_edit_obj)
#3.返回一个编辑数据的页面,并且该页面需要提前展示出原来的数据
return render(request,'userEditPag.html',{'target_edit_obj':target_edit_obj})
def user_delete_func(request):
target_delete_id = request.GET.get('delete_id')
models.User.objects.filter(pk=target_delete_id).delete()
return redirect('/user_list/')
userlistPaga.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.1/jquery.min.js"></script>
{% load static %}
<link rel="stylesheet" href="{% static 'bootstrap-3.4.1-dist/css/bootstrap.min.css' %}">
<script src="{% static 'bootstrap-3.4.1-dist/js/bootstrap.min.js' %}"></script>
</head>
<body>
<div class="container">
<div class="row">
<h1 class="text-center"> 数据展示页</h1>
<div class="col-md-8 col-md-offset-2">
<a href="/user_add/" class="btn btn-primary btn-xs">数据添加</a>
<table class="table table-hover table-striped">
<thead>
<tr >
<th>Id</th>
<th>Name</th>
<th>Age</th>
<th class="text-center" >Operation</th>
</tr>
</thead>
<tbody>
{% for user_obj in user_data %}
<tr>
<td>{{ user_obj.pk }}</td>
<td>{{ user_obj.name }}</td>
<td>{{ user_obj.age }}</td>
<td class="text-center">
<a href="/user_edt/?edit_id={{ user_obj.pk }}" class="btn btn-primary btn-xs" >编辑</a>
{# <a href="/user_delete/?delete_id={{ user_obj.pk }}" class="btn btn-danger btn-xs">删除</a>#}
<a href="/user_delete/?delete_id={{ user_obj.pk }}" class="btn btn-danger btn-xs delBtn" >删除</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
<script>
$('.delBtn').click(function (){
let res = confirm('你确定要删除吗')
if(res){
}else{
return false
}
})
</script>
</body>
</html>
userAddPage.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.1/jquery.min.js"></script>
{% load static %}
<link rel="stylesheet" href="{% static 'bootstrap-3.4.1-dist/css/bootstrap.min.css' %}">
<script src="{% static 'bootstrap-3.4.1-dist/js/bootstrap.min.js' %}"></script>
</head>
<body>
<div class="container">
<div class="row">
<h1 class="text-center"> 数据添加页</h1>
<div class="col-md-8 col-md-2">
<form action="" method="post">
<p>name:
<input type="text" name="name" class="form-control">
</p>
<p>age:
<input type="text" name="age" class="form-control">
</p>
<p>
<input type="submit" value="添加用户" class="btn btn-warning btn-block">
</p>
</form>
</div>
</div>
</div>
</body>
</html>
userEditPage.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.1/jquery.min.js"></script>
{% load static %}
<link rel="stylesheet" href="{% static 'bootstrap-3.4.1-dist/css/bootstrap.min.css' %}">
<script src="{% static 'bootstrap-3.4.1-dist/js/bootstrap.min.js' %}"></script>
</head>
<body>
<div class="container">
<div class="row">
<h1 class="text-center"> 数据编辑页</h1>
<div class="col-md-8 col-md-2">
<form action="" method="post">
<p>name:
<input type="text" name="name" class="form-control" value="{{ target_edit_obj.name }}">
</p>
<p>age:
<input type="text" name="age" class="form-control" value="{{ target_edit_obj.age }}">
</p>
<p>
<input type="submit" value="编辑用户" class="btn btn-primary btn-block">
</p>
</form>
</div>
</div>
</div>
</body>
</html>
django请求生命周期流程图(*****)
学习流程:路由层、视图层、模板层、模型层、组件、BBS项目
django请求生命周期的含义:当用户在浏览器输入URL到用户看到网页的这个时间段内,django后台所发生的事情



Django的请求生命周期流程
1.首先 用户在浏览器中输入url,发送一个GET方法的request请求。
2.Django中封装了socket的WSGI服务器,监听端口接受这个request请求,在进行解析封装,然后传送到中间件中,这个request请求再依次经过中间件。
3.对请求进行效验处理,再传输到路由系统中进行路由分发,匹配相对应的视图函数(FBV),再将request请求传输到views中的这个视图函数中,进行业务逻辑处理。
4.调用modles模型层中表对象,通过orm拿到数据库(DB)的数据。
5.同时拿到templates中相应的模板进行渲染,然后将这个封装了模板response响应传输到中间件中,依次进行处理,最后通过WSGI再进行封装处理,响应给浏览器展示给用户。
django的请求周期分布解析
1.浏览器
发送请求(HTTP协议)
2.web服务网关接口
1.请求来的时候解析封装
响应走的时候打包处理
2.django默认的wsgiref模块不能承受高并发 最大只有1000左右
上线之后会替换成uwsgi来增加并发量
3.WSGI跟wsgiref和uwsgi是什么关系
WSGI是协议
wsgiref和uwsgi是实现该协议的功能模块
3.django后端
1.django中间件(暂时不考虑 后面讲)
类似于django的保安 门户
2.urls.py 路由层
识别路由匹配对应的视图函数
3.views.py 视图层
网站整体的业务逻辑
4.templates文件夹 模版层
网站所有的html文件
5.models.py 模型层
ORM
额外扩展:缓存数据库的作用
缓存数据库
缓存数据库: 提前已经将你想要的数据准备好了 你来直接拿就可以 提高效率和响应时间
django路由层
1.路由匹配
django1.x 第一个参数是正则表达式
django2.x及以上版本 path第一个参数写什么就匹配什么 匹配到直接执行对应的视图函数
path('admin/', admin.site.urls)
路由匹配错误及原因
当浏览器要打开下面的web网页接口 ?
URL=127.0.0.1:8000/testadd
代码展示:
urlpatterns = [
path('test/',views.test),
path('testadd/',views.testadd)
]
原因:按照匹配规则,从上到下只要正则表达式匹配成功,就不会往下执行,当路由匹配到第一个正则(test)就成功匹配了,就不会往下执行了
如何解决上述问题?
方式1:
我们在输入URL默认会加斜杠,因为django内部帮我们做了重定向,一次匹配不行,url后面加斜杠再来一次
按照下面的顺序依次匹配
path('test/',views.test),
path('testadd/',views.testadd)
方式2:
settings配置文件内添加(控制django是否自动添加斜杠匹配
APPEND_SLASH = False/True # 默认是True自动添加斜杠
url方法第一个是参数 的正则表达式
urlpatterns = [
path('^admin/', admin.site.urls),
# 首页 默认进去
path('^$',views.home),
# 路由匹配
path('^test/$',views.test),
path('^testadd/$',views.testadd),
# 尾页(了解 后期异常捕获)
path('',views.error),
]
解析:
^ : 开头
$ : 结尾
2. 正则匹配
django2.X及以上版本有re_path 第一个参数是正则
匹配的本质是只要第一个正则表达式能够从用户输入的路由中匹配到数据就算匹配成功会立刻停止路由层其他的匹配直接执行对应的视图函数
re_path('^test/$', views.test)
re_path('^testadd/$', views.testadd)
django1.X路由匹配使用的是url() 功能与django2.X及以上的re_path()一致
3.转换器
正常情况下很多网站都会有很多相似的网址 如果我们每一个都单独开设路由不合理
django2.X及以上版本路由动态匹配有转换器(五种)
str:匹配除路径分隔符外的任何非空字符串。
int:匹配0或者任意正整数。
slug:匹配任意一个由字母或数字组成的字符串。
uuid:匹配格式化后的UUID。
path:能够匹配完整的URL路径
ps:还支持自定义转换器(自己写正则表达式匹配更加细化的内容)
# 转换器 将对应位置匹配到的数据转换成固定的数据类型
path('index/<str:info>/', views.index_func), # index_func(实参request对象,info='转换器匹配到的类型转换之后的内容')
path('index/<str:info>/<int:id>/', views.index_func) # index_func(实参request对象,info='转换器匹配到的类型转换之后的内容',id='转换器匹配到的类型转换之后的内容')
4.正则匹配的无名有名分组
无名分组
分组就是给某一段正则表达式用小括号括起来
re_path('^test/(\d{4})/', views.test)
无名分组:会将括号内正则表达式匹配到的内容当做位置参数传递给视图函数
代码实现
urls.py
# 会把括号中匹配的数字当成位置参数传给视图函数
path('^test/(\d(4))/',views.test)
views.py
def test(request,xx):
print(xx)
return HttpResponse('test')
正则:
\d(4) : 匹配4个任意数字
有名分组
re_path('^test/(?P<year>\d{4})/', views.test)
有名分组:会将括号内正则表达式匹配到的内容当做关键字参数传递给视图函数
注意上述的分组不能混合使用!!!
代码实现
# 会把括号中匹配的数字当成关键字参数传给视图函数
path('^testadd/(?P<year>\d+)',views.testadd)
def testadd(request,year):
print(year)
return HttpResponse('testadd')
注意:有名分组可以允许有多个分组名,但不允许一个无名分组一个或者多个有名分组存在。即有名分组和无名分组不能混合是使用
单个有名或者无名分组是可以使用多次的
1.无名分组单个使用多次
path('^index/(\d(4))/(\d(4))/(\d(4))/',views.index),
2.有名分组单个使用多次
path('^index/(?P<year>\d+)/(?P<age>\d+)/(?P<month>\d+)/',views.index),
def index(request,*args,**kwargs):
print(args)
return HttpResponse('index')
反向解析
但路由频繁变化的时候,html页面与后端上的连接地址如何做到动态解析呢?
通过在url里面设置路由和视图函数的别名,动态解析出对应的路由,该路由直接执行对应的函数,函数直接执行对应的url
1.概念
通过一些方法得到一个结果 该结果可以直接访问对应的url触发视图函数 实现url路由频繁变化,html界面与后端动态解析连接地址操作步骤:
基本使用
1.方向解析器路由配置
path('login001/', views.login, name='login_view') # name='login_view' 起别名
前后端反向解析
2.导入模块reverse
from django.shortcuts import render,HttpResponse,redirect,reverse
3.反向解析 reverse('login_view')
def home(request):
print(reverse('login_view'))
return render(request,'home.html')
前端反向解析
4.前端模板文件反向解析
<a href="{% url 'login_view' %}">111</a>
动态路由的反向解析
path('func1/<str:others>/', views.func1_func, name='func1_view')
html页面上模板语法 {% url 'func1_views' 'kimi' %}
后端语法 reverse('func1_views', args=('哈哈哈',))

总结三点
1、路由固定写死
1.路由
path('index/',views.index,name='index_view')
2.前端页面
<a href='index/'></a>
{% url 'index_view'%}
3.后端
reverse('index_vies')
2.动态路由解析
1.路由
path('index/<str:info>?',views.index,name='index_view')
2.前端页面
<a href='/index/jason/'></a>
<a href='/index/kevin/'></a>
{% url 'index_view 'jason%} /index/jason/
3.后端
reverse('index_view',args=('tony',)) /index/tony/
3.正则表达式解析
1.路由
re_path('index/(?P<year>\d+)',views.index,name='index_view')
2.前端页面
<a href='/index/1/'><a/>
<a href='/index/123/'><a/>
{% href='index_view' '123'%} /index/123/
3.后端
reverse('index_view',args=('111',)) /index/111/
路由分发
django支持每个应用都可以由自己独立的路由层(url.py)、静态文件(static文件夹)、模板层(templates)。基于该特性多人开发项目就可以完全解耦合,之后利用路由分发还可以整合到一起
为什么要用路由分发?
- 解决项目的总路由匹配关系过多的情况
- 总路由分开于干路由与视图函数的的直接对应关系
- 总路由是一个分发处理(识别当前url是属于哪个应用下的,直接分发对应的应用去处理)
- 当请求来了,总路由不做对应关系,根据请求直接访问哪个app的功能,直接将请求发送给对应的app
- 提前创建好应用app01(创建即注册)、app02,然后记得注册app02
1.总路由分发配置
使用路由分发之前,总路由直接干路由与属兔函数的匹配
方式一:
1.需要导入一个include路由分发模块
from django.conf.urls import url,include
2.导入子路由的urls 为了重名,起别名
from app01 import urls as app01——urls
from app02 import urls as app02——urls
urlpatterns = [
url(r'^admin/', admin.site.urls),
# 1.路由分发
url(r'^app01/',include(app01_urls)), # 只要url前缀是app01开头 全部交给app01处理
url(r'^app02/',include(app02_urls)) # 只要url前缀是app02开头 全部交给app02处理
]
方式二:
1.需要导入一个include路由分发模块
from django.urls import path,include
2.总路由分发 简易>>>推荐
urlpatterns = [
# 路由分发终极写法 简便
path('app01/',include('app01.urls')),
path('app02/',include('app02.urls'))
2.子路由配置
使用路由分发之后,总路由只按照应用名分配匹配方向,如下所示
app01 urls.py
from django.urls import path
from app01 import views
urlpatterns = [
path('index/',views.index)
]
app2 urls.py
from django.urls import path
from app02 import views
urlpatterns = [
path('index/',views.index)
]
app01.html
def index(request):
return HttpResponse('index from app01')
app02.html
def index(request):
return HttpResponse('index from app02')
名称空间

路由分发之后,针对相同的别名是否能自动反向解析不同的应用前缀,默认情况下是无法直接识别应用前缀的,如果想要正常识别应用前缀,有以下两种方式:
方式1:利用名称空间
总路由
path('app01/',include(('app01.urls','app01'),namespace='app01')), # 创建了名称空间app01
path('app02/',include(('app02.urls','app02'),namespace='app02')), # 创建了名称空间app02
反向解析
后端
def index(request):
print(reverse('app01:index_view'))
return HttpResponse ('index from app01')
def index(request):
print(reverse('app02:index_view'))
return HttpResponse reverse('app02:index_view')
前端页面
{% url 'app01:index_view'%}
子路由:
urlpatterns = [
path('index/',views.index,name='index_view')
]
urlpatterns = [
path('index/',views.index,name='index_view')
]
方式2:别名不冲突即可 利用应用名作为别名的前缀
1.总路由
urlpatterns = [
path('admin/', admin.site.urls),
# 路由分发
path('app01/',include('app01.urls')),
path('app02/',include('app02.urls'))
]
2.子路由
urlpatterns = [
path('index/',views.index,name='app01_index_view')
path('index/',views.index,name='app02_index_view')
]
3.后端
reverse('app01_index_view')
reverse('app02_index_view')
总结:只要保证名字不冲突骂我们就没必要使用名称空间,直接在路由起别名的时候加载app名作为别名的前缀

python虚拟环境
什么是虚拟环境?
项目1需要使用:django1.11 python38
项目2需要使用:django2.22 pymysql requests python38
项目3需要使用:django3.22 request_html flask urllib3 python38
虚拟环境:能够针对相同版本的解释器创建多个分身 每个分身可以有自己独立的环境
pycharm创建虚拟环境:(每创建一个虚拟环境就相当于重新下载了一个全新的解释器)
实际开发过程,我们需要给不同的项目配备不同的环境,项目需要用到的环境是什么我们就给装什么样的环境。一般不用的我们不装,因为虚拟环境创建太多(第三方模块或者工具太),是会消耗硬盘空间。
目前我们不使用虚拟环境,所有的模块统一下载到本地
如何创建虚拟环境?
1.终端命令
1.创建一个虚拟环境
python -m venv pyvenv38 # 只能是python
2.激活
activate(cd scripts进入文件内删)
3.关闭
deactivate
注意:python命令此处不支持多版本共存的操作 python27 python36 python38
2.pycharm创建虚拟环境
1、创建虚拟环境
2、虚拟环境标志
3、虚拟环境下载django
4、使用虚拟环境
如果下载过程出现问题了,就复制提示的解决方法到文件上方框里去执行
pip install --index-url http://mirrors.aliyun.com/pypi/simple/ django==1.11.11 --trusted-host mirrors.aliyun.com


浙公网安备 33010602011771号