Django3.2框架
Django3.2
前言
之前我们介绍过web应用程序和http协议,简单了解过web开发的概念。Web应用程序的本质
- 接收并解析HTTP请求,获取具体的请求信息
- 处理本次HTTP请求,即完成本次请求的业务逻辑处理
- 构造并返回处理结果——HTTP响应
|
|
那么什么是web框架呢?
Web应用框架有助于减轻网页开发时共通性活动的工作负荷,例如许多框架提供数据库访问接口、标准样板以及会话管理等,可提升代码的可再用性。
说简单点就是web框架用于搭建Web应用程序,免去不同Web应用相同代码部分的重复。
一、Django介绍
Python下有许多款不同的 Web 框架。Django是重量级选手中最有代表性的一位。许多成功的网站和APP都基于Django。Django 是一个开放源代码的 Web 应用框架,由 Python 写成。Django 遵守 BSD 版权,初次发布于 2005 年 7 月, 并于 2008 年 9 月发布了第一个正式版本 1.0 。
Django 采用了 MVT 的软件设计模式,即模型(Model),视图(View)和模板(Template)。
这个MVT模式并非django首创,在其他的语言里面也有类似的设计模式MVC,甚至可以说django里面的MVT事实上是借鉴了MVC模式衍生出来的。
M,Model,模型,是用于完成操作数据库的。
V,View,视图,里面的代码就是用于展示给客户端的页面效果。
C,Controller,控制器,是一个类或者函数,里面的代码就是用于项目功能逻辑的,一般用于调用模型来获取数据,获取到的数据通过调用视图文件返回给客户端。
而MVT指的是:
- M全拼为Model,与MVC中的M功能相同,负责和数据库交互,进行数据处理。
- V全拼为View,与MVC中的C功能相同,接收请求,进行业务处理,返回应答。
- T全拼为Template,与MVC中的V功能相同,负责封装构造要返回的html。
MVT模型的工作流程
路由控制器将请求转发给对应的视图函数,完成业务逻辑,视图函数将从model中获取的数据嵌入到template的中模板文件(html)渲染成一个页面字符串,返回给客户端的流程。
所以我们学习Django重点是四个部分:url路由器+MVT
二、Django下载与运行
2.1、Django的下载
目前我们学习和使用的版本是3.2LTS版本
|
|
官网: http://www.djangoproject.com
文档:https://docs.djangoproject.com/zh-hans/3.2/
在本地安装
|
|
|
|
|
|
当然在以后开发或者学习中,我们肯定都会遇到在一台开发机子中,运行多个项目的情况,有时候还会出现每个项目的python解析器或者依赖包的版本有差异.
2.2、Django的启动运行
创建虚拟环境并在虚拟环境中下载安装django包
|
|
完成了以后,直接直接下pycharm下面的终端terminal中使用命令运行django
|
|
在浏览器中访问显示的地址http://127.0.0.1:8090
.效果如下则表示正确安装了.
runserver默认启动的wsgi.py文件作为web服务器接口
2.3、创建应用
创建自应用:
python manage.py startapp 子应用名称
Django完整的目录结构如下:
|
|
当然如果每次运行项目都要在终端下输入命令的话,很麻烦,这时候我们可以借助pycharm直接自动运行这段命令.当然,这个需要我们在pycharm配置一下的.
(小三角形)
可以在runserver 参数后配置修改django监听的端口和IP地址,当然,只能是127.0.0.1对应的其他地址.不能是任意IP.否则无法运行或访问!!
2.4、快速使用Django
在django中要提供数据展示给用户,我们需要完成3个步骤.
需求:利用Django实现一个查看当前时间的web页面。
基于MTV模型,设计步骤如下:
- step1:在urls.py中设计url与视图的映射关系。
- step2:创建子应用,在views.py中构建视图函数。
- step3:将变量嵌入到模板中返回客户端。
(1)创建子应用
|
|
子应用的名称将来会作为目录名而存在,所以不能出现特殊符号,不能出现中文等多字节的字符.
(2) 绑定路由
demo/urls.py
代码:
|
|
(3)视图函数
home/view.py
,代码:
|
|
(4)构建模板
|
|
因为上面我们绑定index视图函数的url地址是index,所以我们可以通过http://127.0.0.1:8000/
拼接url地址index
来访问视图函数
三、路由控制器
Route路由, 是一种映射关系!路由是把客户端请求的url路径和用户请求的应用程序[这里意指django里面的视图进行绑定映射的一种关系。
请求路径和视图函数不是一对一映射关系!
在django中所有的路由最终都被保存到一个变量 urlpatterns.
, urlpatterns必须声明在主应用下的urls.py总路由中。这是由配置文件settings设置的。
在django运行中,当客户端发送了一个http请求到服务端,服务端的web服务器则会从http协议中提取url地址, 从程序内部找到项目中添加到urlpatterns里面的所有路由信息的url进行遍历匹配。如果相等或者匹配成功,则调用当前url对象的视图方法。
在给urlpatterns路由列表添加路由的过程中,django一共提供了2个函数给开发者注册路由.
|
|
(1)基本使用
|
|
(2)路由分发
|
|
(3)路由转发器
有时候上面的内置的url转换器并不能满足我们的需求,因此django给我们提供了一个接口可以让我们自己定义自己的url转换器。
|
|
path("index/<mobile:mobile>",index)
def index(request,mobile):
print(":::",type(mobile))
return HttpResponse(f"hi,{mobile}用户")
(4)反向解析
在使用Django 项目时,一个常见的需求是获得URL 的最终形式,以用于嵌入到生成的内容中(视图中和显示给用户的URL等)或者用于处理服务器端的导航(重定向等)。人们强烈希望不要硬编码这些URL(费力、不可扩展且容易产生错误)或者设计一种与URLconf 毫不相关的专门的URL 生成机制,因为这样容易导致一定程度上产生过期的URL。
在需要URL 的地方,对于不同层级,Django 提供不同的工具用于URL 反查:
- 在模板中:使用url模板标签
- 在Python 代码中:使用from django.urls import reverse 函数。
urls.py中为url设置别名参数:
|
|
应用之在模板中反向解析:
|
|
应用之在py文本中反向解析:
|
|
四、视图
django的视图主要有2种,分别是函数视图和类视图.现在刚开始学习django,我们先学习函数视图(FBV),后面再学习类视图[CBV].
4.1、请求方式
web项目运行在http协议下,默认肯定也支持用户通过不同的http请求发送数据来。django支持让客户端只能通过指定的Http请求来访问到项目的视图
home/views.py
,代码:
|
|
路由绑定,demo/urls.py
,代码:
|
|
通过浏览器,访问效果http://127.0.0.1:8090/login
:
4.2、请求对象
django将请求报文中的请求行、首部信息、内容主体封装成 HttpRequest 类中的属性。 除了特殊说明的之外,其他均为只读的。
(1)请求方式
|
|
(2)请求数据
|
|
(3)请求路径
|
|
(4)请求头
|
|
4.3、响应对象
响应对象主要有三种形式:
- HttpResponse()
- render()
- redirect()
(1)HttpResponse()
Django服务器接收到客户端发送过来的请求后,会将提交上来的这些数据封装成一个 HttpRequest 对象传给视图函数。那么视图函数在处理完相关的逻辑后,也需要返回一个响应给浏览器。而这个响应,我们必须返回 HttpResponseBase 或者他的子类的对象。而 HttpResponse 则是 HttpResponseBase 用得最多的子类。
常用属性:
- content:返回的内容。
- status:返回的HTTP响应状态码。
- content_type:返回的数据的MIME类型,默认为 text/html 。浏览器会根据这个属性,来显示数据。如果是 text/html ,那么就会解析这个字符串,如果 text/plain ,那么就会显示一个纯文本。
- 设置响应头: response[‘X-Access-Token’] = ‘xxxx’ 。
JsonResponse类:
用来对象 dump 成 json 字符串,然后返回将 json 字符串封装成 Response 对象返回给浏览器。并且他的 Content-Type 是 application/json 。示例代码如下:
|
|
默认情况下 JsonResponse 只能对字典进行 dump ,如果想要对非字典的数据进行 dump ,那么需要给 JsonResponse 传递一个 safe=False 参数。示例代码如下:
(2)render()
|
|
参数:
|
|
render方法就是将一个模板页面中的模板语法进行渲染,最终渲染成一个html页面作为响应体。
(3)redirect方法
当您使用Django框架构建Python Web应用程序时,您在某些时候必须将用户从一个URL重定向到另一个URL,
通过redirect方法实现重定向。
参数可以是:
- 一个绝对的或相对的URL, 将原封不动的作为重定向的位置.
- 一个url的别名: 可以使用reverse来反向解析url
传递要重定向到的一个具体的网址
|
|
当然也可以是一个完整的网址
|
|
传递一个视图的名称
|
|
APPEND_SLASH的实现就是基于redirect
4.4、登录验证案例
from django.contrib import admin
from django.urls import path, re_path,include
from users.views import index,login,auth
urlpatterns = [
path("",index),
path("login",login),
path("auth",auth),
]
def login(request):
return render(request,"users/login.html")
def auth(request):
# 获取数据
print("request.POST:",request.POST)
user = request.POST.get("user")
pwd = request.POST.get("pwd")
# 模拟数据校验
if user == "rain" and pwd == "123":
# return HttpResponse("验证通过")
return redirect("/users/")
else:
# return HttpResponse("用户名或者密码错误")
# return redirect("/users/login")
msg = "用户名或者密码错误"
return render(request,"users/login.html",{"msg":msg})
|
|
五、模板语法
模板引擎是一种可以让开发者把服务端数据填充到html网页中完成渲染效果的技术。它实现了把前端代码和服务端代码分离的作用,让项目中的业务逻辑代码和数据表现代码分离,让前端开发者和服务端开发者可以更好的完成协同开发。
静态网页:页面上的数据都是写死的,万年不变
动态网页:页面上的数据是从后端动态获取的(比如后端获取当前时间;后端获取数据库数据然后传递给前端页面)
Django框架中内置了web开发领域非常出名的一个DjangoTemplate模板引擎(DTL)。DTL官方文档
要在django框架中使用模板引擎把视图中的数据更好的展示给客户端,需要完成3个步骤:
在项目配置文件中指定保存模板文件的模板目录。一般模板目录都是设置在项目根目录或者主应用目录下。
在视图中基于django提供的渲染函数绑定模板文件和需要展示的数据变量
在模板目录下创建对应的模板文件,并根据模板引擎内置的模板语法,填写输出视图传递过来的数据。
配置模板目录:在当前项目根目录下创建了模板目录templates. 然后在settings.py, 模板相关配置,找到TEMPLATES配置项,填写DIRS设置模板目录。
|
|
5.1、简单案例
为了方便接下里的演示内容,我这里创建创建一个新的子应用tem
|
|
settings.py,注册子应用,代码:
|
|
总路由加载子应用路由,urls.py
,代码:
|
|
在子应用目录下创建urls.py子路由文件,代码如下:
|
|
tem.views.index,代码:
|
|
templates.index.html,代码:
|
|
5.2、render函数内部本质
|
|
- DTL模板文件与普通html文件的区别在哪里?
DTL模板文件是一种带有特殊语法的HTML文件,这个HTML文件可以被Django编译,可以传递参数进去,实现数据动态化。在编译完成后,生成一个普通的HTML文件,然后发送给客户端。
- 开发中,我们一般把开发中的文件分2种,分别是静态文件和动态文件。
* 静态文件,数据保存在当前文件,不需要经过任何处理就可以展示出去。普通html文件,图片,视频,音频等这一类文件叫静态文件。 * 动态文件,数据并不在当前文件,而是要经过服务端或其他程序进行编译转换才可以展示出去。 编译转换的过程往往就是使用正则或其他技术把文件内部具有特殊格式的变量转换成真实数据。 动态文件,一般数据会保存在第三方存储设备,如数据库中。django的模板文件,就属于动态文件。
5.3、模板语法
变量渲染(深度查询、过滤器)
1 2
{{val}} {{val|filter_name:参数}}
标签
{% tag_name %}
嵌套和继承
5.3.1、变量渲染之深度查询
|
|
模板代码,templates/index.html
:
|
|
通过句点符号深度查询
tem.urls
,代码:
|
|
5.3.2、变量渲染之内置过滤器
语法:
|
|
内置过滤器
过滤器 | 用法 | 代码 |
---|---|---|
last | 获取列表/元组的最后一个成员 | {{liast | last}} |
first | 获取列表/元组的第一个成员 | {{list|first}} |
length | 获取数据的长度 | {{list | length}} |
defualt | 当变量没有值的情况下, 系统输出默认值, | {{str|default=“默认值”}} |
safe | 让系统不要对内容中的html代码进行实体转义 | {{htmlcontent| safe}} |
upper | 字母转换成大写 | {{str | upper}} |
lower | 字母转换成小写 | {{str | lower}} |
title | 每个单词首字母转换成大写 | {{str | title}} |
date | 日期时间格式转换 | `{{ value |
cut | 从内容中截取掉同样字符的内容 | {{content | cut:“hello”}} |
list | 把内容转换成列表格式 | {{content | list}} |
add | 加法 | {{num| add}} |
filesizeformat | 把文件大小的数值转换成单位表示 | {{filesize | filesizeformat}} |
join |
按指定字符拼接内容 | {{list| join("-")}} |
random |
随机提取某个成员 | {list | random}} |
slice |
按切片提取成员 | {{list | slice:":-2"}} |
truncatechars |
按字符长度截取内容 | {{content | truncatechars:30}} |
truncatewords |
按单词长度截取内容 | 同上 |
过滤器的使用
|
|
5.3.3、自定义过滤器
虽然官方已经提供了许多内置的过滤器给开发者,但是很明显,还是会有存在不足的时候。例如:希望输出用户的手机号码时, 13912345678 —-» 139*****678
,这时我们就需要自定义过滤器。要声明自定义过滤器并且能在模板中正常使用,需要完成2个前置的工作:
|
|
5.3.4、标签
(1)if 标签
视图代码,tem.views.py:
|
|
模板代码,templates/index.html,代码:
|
|
路由代码:
|
|
(2)for标签
视图代码, home.views.py:
|
|
template/index.html,代码:
|
|
路由代码:
|
|
循环中, 模板引擎提供的forloop对象,用于给开发者获取循环次数或者判断循环过程的.
属性 | 描述 |
---|---|
forloop.counter | 显示循环的次数,从1开始 |
forloop.counter0 | 显示循环的次数,从0开始 |
forloop.revcounter0 | 倒数显示循环的次数,从0开始 |
forloop.revcounter | 倒数显示循环的次数,从1开始 |
forloop.first | 判断如果本次是循环的第一次,则结果为True |
forloop.last | 判断如果本次是循环的最后一次,则结果为True |
forloop.parentloop | 在嵌套循环中,指向当前循环的上级循环 |
5.3.5、模板嵌套继承
传统的模板分离技术,依靠{% include “模板文件名”%}实现,这种方式,虽然达到了页面代码复用的效果,但是由此也会带来大量的碎片化模板,导致维护模板的成本上升.因此, Django框架中除了提供这种模板分离技术以外,还并行的提供了 模板继承给开发者.
|
|
(1) 继承父模板的公共内容
{% extends “base.html” %}
视图, home.views.py代码:
|
|
子模板, templates/index.html
|
|
父模板, templates/base.html
|
|
(2) 个性展示不同于父模板的内容
{%block %} 独立内容 {%endblock%}
{{block.super}}
视图home.views.py, 代码:
|
|
路由 home.urls.py,代码:
|
|
子模板index.html,代码:
|
|
子模板home.html,代码:
|
|
父模板base.html,代码:
|
|
- 如果你在模版中使用
{% extends %}
标签,它必须是模版中的第一个标签。其他的任何情况下,模版继承都将无法工作。- 在base模版中设置越多的
{% block %}
标签越好。请记住,子模版不必定义全部父模版中的blocks,所以,你可以在大多数blocks中填充合理的默认内容,然后,只定义你需要的那一个。多一点钩子总比少一点好。- 为了更好的可读性,你也可以给你的
{% endblock %}
标签一个 名字 。例如:{``%
block content``%``}``...``{``%
endblock content``%``},
在大型模版中,这个方法帮你清楚的看到哪一个{% block %}
标签被关闭了。- 不能在一个模版中定义多个相同名字的
block
标签。
5.4、静态文件
开发中在开启了debug模式时,django可以通过配置,允许用户通过对应的url地址访问django的静态文件。
setting.py,代码:
|
|
总路由,urls.py,代码:
|
|
注意:项目上线以后,关闭debug模式时,django默认是不提供静态文件的访问支持,项目部署的时候,我们会通过收集静态文件使用nginx这种web服务器来提供静态文件的访问支持。
六、模型层(ORM)
Django中内嵌了ORM框架,不需要直接编写SQL语句进行数据库操作,而是通过定义模型类,操作模型类来完成对数据库中表的增删改查和创建等操作。
O是object,也就类对象的意思。
R是relation,翻译成中文是关系,也就是关系数据库中数据表的意思。
M是mapping,是映射的意思。
映射:
类:sql语句table表
类成员变量:table表中的字段、类型和约束
类对象:sql表的表记录
ORM的优点
数据模型类都在一个地方定义,更容易更新和维护,也利于重用代码。
ORM 有现成的工具,很多功能都可以自动完成,比如数据消除、预处理、事务等等。
它迫使你使用 MVC 架构,ORM 就是天然的 Model,最终使代码更清晰。
基于 ORM 的业务代码比较简单,代码量少,语义性好,容易理解。
新手对于复杂业务容易写出性能不佳的 SQL,有了ORM不必编写复杂的SQL语句, 只需要通过操作模型对象即可同步修改数据表中的数据.
开发中应用ORM将来如果要切换数据库.只需要切换ORM底层对接数据库的驱动【修改配置文件的连接地址即可】
ORM 也有缺点
- ORM 库不是轻量级工具,需要花很多精力学习和设置,甚至不同的框架,会存在不同操作的ORM。
- 对于复杂的业务查询,ORM表达起来比原生的SQL要更加困难和复杂。
- ORM操作数据库的性能要比使用原生的SQL差。
- ORM 抽象掉了数据库层,开发者无法了解底层的数据库操作,也无法定制一些特殊的 SQL。【自己使用pymysql另外操作即可,用了ORM并不表示当前项目不能使用别的数据库操作工具了。】
我们可以通过以下步骤来使用django的数据库操作
|
|
6.1、配置数据库连接
在settings.py中保存了数据库的连接配置信息,Django默认初始配置使用sqlite数据库。
-
使用MySQL数据库首先需要安装驱动程序
1
pip install PyMySQL
-
在Django的工程同名子目录的
__init__
.py文件中添加如下语句1 2
from pymysql import install_as_MySQLdb install_as_MySQLdb() # 让pymysql以MySQLDB的运行模式和Django的ORM对接运行
作用是让Django的ORM能以mysqldb的方式来调用PyMySQL。
-
修改DATABASES配置信息
1 2 3 4 5 6 7 8 9 10
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'HOST': '127.0.0.1', # 数据库主机 'PORT': 3306, # 数据库端口 'USER': 'root', # 数据库用户名 'PASSWORD': '123', # 数据库用户密码 'NAME': 'student' # 数据库名字 } }
-
在MySQL中创建数据库
1 2
create database student; # mysql8.0默认就是utf8mb4; create database student default charset=utf8mb4; # mysql8.0之前的版本
-
注意3: 如果想打印orm转换过程中的sql,需要在settings中进行如下配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
LOGGING = { 'version': 1, 'disable_existing_loggers': False, 'handlers': { 'console':{ 'level':'DEBUG', 'class':'logging.StreamHandler', }, }, 'loggers': { 'django.db.backends': { 'handlers': ['console'], 'propagate': True, 'level':'DEBUG', }, } }
6.2、定义模型类
定义模型类
- 模型类被定义在"子应用/models.py"文件中。
- 模型类必须直接或者间接继承自django.db.models.Model类。
接下来以学生管理为例进行演示。[系统大概3-4表,学生信息,课程信息,老师信息],创建子应用student,注册子应用并引入子应用路由.
settings.py,代码:
|
|
urls.py,总路由代码:
|
|
在models.py 文件中定义模型类。
|
|
(1) 数据库表名
模型类如果未指明表名db_table,Django默认以 小写app应用名_小写模型类名 为数据库表名。
可通过db_table 指明数据库表名。
(2) 关于主键
django会为表创建自动增长的主键列,每个模型只能有一个主键列。
如果使用选项设置某个字段的约束属性为主键列(primary_key)后,django不会再创建自动增长的主键列。
|
|
默认创建的主键列属性为id,可以使用pk代替,pk全拼为primary key。
(3) 属性命名限制
-
不能是python的保留关键字。
-
不允许使用连续的2个下划线,这是由django的查询方式决定的。__ 是关键字来的,不能使用!!!
-
定义属性时需要指定字段类型,通过字段类型的参数指定选项,语法如下:
1
属性名 = models.字段类型(约束选项, verbose_name="注释")
(4)字段类型
类型 | 说明 |
---|---|
AutoField | 自动增长的IntegerField,通常不用指定,不指定时Django会自动创建属性名为id的自动增长属性 |
BooleanField | 布尔字段,值为True或False |
NullBooleanField | 支持Null、True、False三种值 |
CharField | 字符串,参数max_length表示最大字符个数,对应mysql中的varchar |
TextField | 大文本字段,一般大段文本(超过4000个字符)才使用。 |
IntegerField | 整数 |
DecimalField | 十进制浮点数, 参数max_digits表示总位数, 参数decimal_places表示小数位数,常用于表示分数和价格 Decimal(max_digits=7, decimal_places=2) ==> 99999.99~ 0.00 |
FloatField | 浮点数 |
DateField | 日期参数auto_now表示每次保存对象时,自动设置该字段为当前时间。参数auto_now_add表示当对象第一次被创建时自动设置当前。参数auto_now_add和auto_now是相互排斥的,一起使用会发生错误。 |
TimeField | 时间,参数同DateField |
DateTimeField | 日期时间,参数同DateField |
FileField | 上传文件字段,django在文件字段中内置了文件上传保存类, django可以通过模型的字段存储自动保存上传文件, 但是, 在数据库中本质上保存的仅仅是文件在项目中的存储路径!! |
ImageField | 继承于FileField,对上传的内容进行校验,确保是有效的图片 |
(5)约束选项
选项 | 说明 |
---|---|
null | 如果为True,表示允许为空,默认值是False。相当于python的None |
blank | 如果为True,则该字段允许为空白,默认值是False。 相当于python的空字符串,“” |
db_column | 字段的名称,如果未指定,则使用属性的名称。 |
db_index | 若值为True, 则在表中会为此字段创建索引,默认值是False。 相当于SQL语句中的key |
default | 默认值,当不填写数据时,使用该选项的值作为数据的默认值。 |
primary_key | 如果为True,则该字段会成为模型的主键,默认值是False,一般不用设置,系统默认设置。 |
unique | 如果为True,则该字段在表中必须有唯一值,默认值是False。相当于SQL语句中的unique |
注意:null是数据库范畴的概念,blank是表单验证范畴的
(6) 外键
在设置外键时,需要通过on_delete选项指明主表删除数据时,对于外键引用表数据如何处理,在django.db.models中包含了可选常量:
-
CASCADE 级联,删除主表数据时连通一起删除外键表中数据
-
PROTECT 保护,通过抛出ProtectedError异常,来阻止删除主表中被外键应用的数据
-
SET_NULL 设置为NULL,仅在该字段null=True允许为null时可用
-
SET_DEFAULT 设置为默认值,仅在该字段设置了默认值时可用
-
SET() 设置为特定值或者调用特定方法,例如:
1 2 3 4 5 6 7 8 9 10 11 12
from django.conf import settings from django.contrib.auth import get_user_model from django.db import models def get_sentinel_user(): return get_user_model().objects.get_or_create(username='deleted')[0] class UserModel(models.Model): user = models.ForeignKey( settings.AUTH_USER_MODEL, on_delete=models.SET(get_sentinel_user), )
-
DO_NOTHING 不做任何操作,如果数据库前置指明级联性,此选项会抛出IntegrityError异常
商品分类表
id | category | |
---|---|---|
1 | 蔬菜 | |
2 | 电脑 |
商品信息表
id | goods_name | cid |
---|---|---|
1 | 冬瓜 | 1 |
2 | 华为笔记本A1 | 2 |
3 | 茄子 | 1 |
当模型字段的on_delete=CASCADE, 删除蔬菜(id=1),则在外键cid=1的商品id1和3就被删除。
当模型字段的on_delete=PROTECT,删除蔬菜,mysql自动检查商品信息表,有没有cid=1的记录,有则提示必须先移除掉商品信息表中,id=1的所有记录以后才能删除蔬菜。
当模型字段的on_delete=SET_NULL,删除蔬菜以后,对应商品信息表,cid=1的数据的cid全部被改成cid=null
当模型字段的on_delete=SET_DEFAULT,删除蔬菜以后,对应商品信息表,cid=1的数据记录的cid被被设置默认值。
6.3、数据迁移
将模型类定义表架构的代码转换成SQL同步到数据库中,这个过程就是数据迁移。django中的数据迁移,就是一个类,这个类提供了一系列的终端命令,帮我们完成数据迁移的工作。
(1)生成迁移文件
所谓的迁移文件, 是类似模型类的迁移类,主要是描述了数据表结构的类文件.
|
|
(2)同步到数据库中
|
|
补充:在django内部提供了一系列的功能,这些功能也会使用到数据库,所以在项目搭建以后第一次数据迁移的时候,会看到django项目中其他的数据表被创建了。其中就有一个django内置的admin站点管理。
# admin站点默认是开启状态的,我们可以通过http://127.0.0.1:8000/admin
# 这个站点必须有个管理员账号登录,所以我们可以在第一次数据迁移,有了数据表以后,就可以通过以下终端命令来创建一个超级管理员账号。
python manage.py createsuperuser
(3)添加测试数据
|
|
|
|
6.4、数据库基本操作
6.4.1、添加记录
(1)save方法
通过创建模型类对象,执行对象的save()方法保存到数据库中。
|
|
(2)create方法
通过模型类.objects.create()保存,返回生成的模型类对象。
|
|
6.4.2、基础查询
ORM中针对查询结果的限制,提供了一个查询集[QuerySet].这个QuerySet,是ORM中针对查询结果进行保存数据的一个类型,我们可以通过了解这个QuerySet进行使用,达到查询优化,或者限制查询结果数量的作用。
(1)all()
查询所有对象,返回queryset对象。查询集,也称查询结果集、QuerySet,表示从数据库中获取的对象集合。
|
|
(2)filter()
筛选条件相匹配的对象,返回queryset对象。
|
|
(3)get()
返回与所给筛选条件相匹配的对象,返回结果有且只有一个, 如果符合筛选条件的对象超过一个或者没有都会抛出错误。
|
|
(4)first()、last()
分别为查询集的第一条记录和最后一条记录
|
|
(5)exclude()
筛选条件不匹配的对象,返回queryset对象。
|
|
(6)order_by()
对查询结果排序
|
|
(7)count()
查询集中对象的个数
|
|
(8)values()、values_list()
-
value()
把结果集中的模型对象转换成字典,并可以设置转换的字段列表,达到减少内存损耗,提高性能 -
values_list()
: 把结果集中的模型对象转换成列表,并可以设置转换的字段列表(元祖),达到减少内存损耗,提高性能
|
|
6.4.3、模糊查询
(1)模糊查询之contains
说明:如果要包含%无需转义,直接写即可。
例:查询姓名包含’华’的学生。
|
|
(2)模糊查询之startswith、endswith
例:查询姓名以’文’结尾的学生
|
|
以上运算符都区分大小写,在这些运算符前加上i表示不区分大小写,如iexact、icontains、istartswith、iendswith.
(3)模糊查询之isnull
例:查询个性签名不为空的学生。
|
|
(4)模糊查询之in
例:查询编号为1或3或5的学生
|
|
(5)模糊查询之比较查询
- gt 大于 (greater then)
- gte 大于等于 (greater then equal)
- lt 小于 (less then)
- lte 小于等于 (less then equal)
例:查询编号大于3的学生
|
|
(6)模糊查询之日期查询
year、month、day、week_day、hour、minute、second:对日期时间类型的属性进行运算。
例:查询2010年被添加到数据中的学生。
|
|
例:查询2016年6月20日后添加的学生信息。
|
|
6.4.4、进阶查询
(1) F查询
之前的查询都是对象的属性与常量值比较,两个属性怎么比较呢? 答:使用F对象,被定义在django.db.models中。
语法如下:
|
|
(2) Q查询
多个过滤器逐个调用表示逻辑与关系,同sql语句中where部分的and关键字。
例:查询年龄大于20,并且编号小于30的学生。
|
|
如果需要实现逻辑或or的查询,需要使用Q()对象结合|运算符,Q对象被义在django.db.models中。
语法如下:
|
|
例:查询年龄小于19或者大于20的学生,使用Q对象如下。
|
|
Q对象可以使用&、|连接,&表示逻辑与,|表示逻辑或**
例:查询年龄大于20,或编号小于30的学生,只能使用Q对象实现
|
|
Q对象左边可以使用~操作符,表示非not。但是工作中,我们只会使用Q对象进行或者的操作,只有多种嵌套复杂的查询条件才会使用&和~进行与和非得操作
。
例:查询编号不等于30的学生。
|
|
(3)聚合查询
使用aggregate()过滤器调用聚合函数。聚合函数包括:Avg 平均,Count 数量,Max 最大,Min 最小,Sum 求和,被定义在django.db.models中。
例:查询学生的平均年龄。
|
|
注意:aggregate的返回值是一个字典类型,格式如下:
|
|
使用count时一般不使用aggregate()过滤器。
例:查询学生总数。
|
|
(4)分组查询
|
|
(5)原生查询
执行原生SQL语句,也可以直接跳过模型,才通用原生pymysql.
|
|
6.4.5、修改记录
(1) 使用save更新数据
|
|
(2)update更新(推荐)
使用模型类.objects.filter().update(),会返回受影响的行数
|
|
6.4.6、删除记录
删除有两种方法
(1)模型类对象.delete
|
|
(2)模型类.objects.filter().delete()
|
|
代码:
|
|
6.5、创建关联模型
|
|
实例:我们来假定下面这些概念,字段和关系
- 班级模型: 班级名称、导员。
- 课程模型:课程名称、讲师等。
- 学生模型: 学生有姓名,年龄,只有一个班级,所以和班级表是一对多的关系(one-to-many);选修了多个课程,所以和课程表是多对多的关系(many-to-many)
- 学生详情:学生的家庭地址,手机号,邮箱等详细信息,和学生模型应该是一对一的关系(one-to-one)
模型建立如下:
|
|
6.6、关联添加
(1)一对多与一对一
|
|
(2)多对多
|
|
6.7、关联查询
6.7.1、基于对象查询(子查询)
|
|
(1)正向查询按字段
(2)反向查询按表名小写或者related_name
6.7.2、基于双下划线查询(join查询)
|
|
(1)正向关联按关联字段
(2)反向按表名小写或related_name
6.7.3、关联分组查询
|
|
6.8、项目练习
七、Ajax请求
客户端(浏览器)向服务端发起请求的形式:
- 地址栏:GET
- 超链接标签:GET
- form表单:GET或POST
- Ajax(重要):GET或POST或PUT或DELETE
AJAX(Asynchronous Javascript And XML)翻译成中文就是“异步Javascript和XML”。即使用Javascript语言与服务器进行异步交互,传输的数据为XML(当然,传输的数据不只是XML,现在更多使用json数据)。
AJAX的特点和优点:
- 异步
- 局部刷新
应用:
7.2、json数据
|
|
(1)python的序列化和反序列化方法
|
|
(2)Django支持的序列化方法
关于Django中的序列化主要应用在将数据库中检索的数据返回给客户端用户,特别的Ajax请求一般返回的为Json格式。
|
|
7.3、Ajax请求案例
(1) 视图
|
|
(2) 模板:reg.html
|
|
(3) 流程图
练习:
- 计算器
- ajxa通过orm获取书籍并展示
7.4、同源策略
7.4.1、同源策略和跨域
现在我们将reg.html单独放在客户端,用浏览器打开,再触发事件,会发现报错:
这是因为浏览器的同源策略导致的。
同源策略(Same origin policy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。可以说Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。
同源策略,它是由Netscape提出的一个著名的安全策略。现在所有支持JavaScript 的浏览器都会使用这个策略。所谓同源是指,域名,协议,端口相同。当一个浏览器的两个tab页中分别打开来 百度和谷歌的页面当浏览器的百度tab页执行一个脚本的时候会检查这个脚本是属于哪个页面的,即检查是否同源,只有和百度同源的脚本才会被执行。如果非同源,那么在请求数据时,浏览器会在控制台中报一个异常,提示拒绝访问。
那么如何解决这种跨域问题呢,我们主要由三个思路:
- jsonp
- cors
- 前端代理
这里主要给大家介绍第二个:cors
CORS需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能,IE浏览器不能低于IE10。
整个CORS通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。
因此,实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信。
所以,服务器方面只要添加一个响应头,同意跨域请求,浏览器也就不再拦截:
|
|
7.4.2、cors
cors有两种请求:简单请求和非简单请求
只要同时满足以下两大条件,就属于简单的请求
|
|
凡是不同时满足上面两个条件,就属于非简单请求。浏览器对这两种请求的处理,是不一样的。
简单请求:一次请求
非简单请求:两次请求,在发送数据之前会先发一次请求用于做“预检”,只有“预检”通过后才再发送一次请求用于数据传输。
- 请求方式:OPTIONS
- “预检”其实做检查,检查如果通过则允许传输数据,检查不通过则不再发送真正想要发送的消息
- 如何“预检”
=> 如果复杂请求是PUT等请求,则服务端需要设置允许某请求,否则“预检”不通过
Access-Control-Request-Method
=> 如果复杂请求设置了请求头,则服务端需要设置允许某请求头,否则“预检”不通过
Access-Control-Request-Headers
支持跨域,简单请求:
|
|
支持跨域复杂请求:
由于复杂请求时,首先会发送“预检”请求,如果“预检”成功,则发送真实数据。
“预检”请求时,允许请求方式则需服务器设置响应头:Access-Control-Request-Method
“预检”请求时,允许请求头则需服务器设置响应头:Access-Control-Request-Headers
cors在Django中的实现:
在返回结果中加入允许信息(简单请求):
|
|
放到中间件中处理复杂和简单请求:
|
|
在settings中配置即可,在中间件中的位置可以随意放置.
也可以通过第三方组件:pip install django-cors-headers
|
|
前端项目设置请求头记得添加到CORS_ALLOW_HEADERS
八、Django的组件
8.1、中间件
中间件顾名思义,是介于request与response处理之间的一道处理过程,相对比较轻量级,并且在全局上改变django的输入与输出。因为改变的是全局,所以需要谨慎实用,用不好会影响到性能。
Django的中间件的定义:
|
|
如果你想修改请求,例如被传送到view中的HttpRequest对象。 或者你想修改view返回的HttpResponse对象,这些都可以通过中间件来实现。
django框架内部声明了很多的中间件,这些中间件有着各种各种的用途,有些没有被使用,有些被默认开启使用了。而被开启使用的中间件,都是在settngs.py的MIDDLEWARE中注册使用的。
Django默认的Middleware
:
|
|
8.1.1.自定义中间件
(1)定义中间件
创建存放自定义中间件的文件这里选择在app01里创建mdws.py文件:
|
|
- process_request默认返回None,返回None,则继续执行下一个中间件的process_request;一旦返回响应体对象,则会拦截返回。
- process_response必须有一个形参response,并return response;这是view函数返回的响应体,像接力棒一样传承给最后的客户端。
(2) 注册中间件
|
|
(3)构建index路由
|
|
启动项目,访问index路径:
后台打印结果:
|
|
所以,通过结果我们看出中间件的执行顺序:
8.1.3.中间件应用
1. 做IP访问频率限制
某些IP访问服务器的频率过高,进行拦截,比如限制每分钟不能超过20次。
8.2、Cookie与Session
我们知道HTTP协议是无状态协议,也就是说每个请求都是独立的!无法记录前一次请求的状态。但HTTP协议中可以使用Cookie来完成会话跟踪!在Web开发中,使用session来完成会话跟踪,session底层依赖Cookie技术。
8.2.1、cookie
Cookie翻译成中文是小甜点,小饼干的意思。在HTTP中它表示服务器送给客户端浏览器的小甜点。其实Cookie是key-value结构,类似于一个python中的字典。随着服务器端的响应发送给客户端浏览器。然后客户端浏览器会把Cookie保存起来,当下一次再访问服务器时把Cookie再发送给服务器。 Cookie是由服务器创建,然后通过响应发送给客户端的一个键值对。客户端会保存Cookie,并会标注出Cookie的来源(哪个服务器的Cookie)。当客户端向服务器发出请求时会把所有这个服务器Cookie包含在请求中发送给服务器,这样服务器就可以识别客户端了!
cookie可以理解为每一个浏览器针对每一个服务器创建的key-value结构的本地存储文件
(1)cookie流程图
(2)cookie语法
|
|
8.2.2、session
Django 提供对匿名会话(session)的完全支持。这个会话框架让你可以存储和取回每个站点访客任意数据。它在服务器端存储数据, 并以cookies的形式进行发送和接受数据。
(1)session流程图
(2)session语法与案例
1 2 3 4 5 6 7 8
# 1、设置Sessions值 request.session['session_name'] ="admin" # 2、获取Sessions值 session_name = request.session["session_name"] # 3、删除Sessions值 del request.session["session_name"] # 4、flush() # 删除当前的会话数据并删除会话的Cookie。这用于确保前面的会话数据不可以再次被用户的浏览器访问
|
|
- session 在服务器端,cookie 在客户端(浏览器)
- session 默认被存在在服务器的一个文件里(不是内存)
- session 的运行依赖 session id,而 session id 是存在 cookie 中的.
- session 可以放在 文件、数据库、或内存中都可以。
- 用户验证这种场合一般会用 session
(3)session配置
|
|
8.2.3、用户认证组件
Django默认已经提供了认证系统Auth模块,我们认证的时候,会使用auth模块里面给我们提供的表。认证系统包含:
- 用户管理
- 权限
- 用户组
- 密码哈希系统
- 用户登录或内容显示的表单和视图
- 一个可插拔的后台系统 admin
(1)Django用户模型类
Django认证系统中提供了用户模型类User保存用户的数据,默认的User包含以下常见的基本字段:
字段名 | 字段描述 |
---|---|
username |
必选。150个字符以内。 用户名可能包含字母数字,_ ,@ ,+ . 和- 个字符。 |
first_name |
可选(blank=True )。 少于等于30个字符。 |
last_name |
可选(blank=True )。 少于等于30个字符。 |
email |
可选(blank=True )。 邮箱地址。 |
password |
必选。 密码的哈希加密串。 (Django 不保存原始密码)。 原始密码可以无限长而且可以包含任意字符。 |
groups |
与Group 之间的多对多关系。 |
user_permissions |
与Permission 之间的多对多关系。 |
is_staff |
布尔值。 设置用户是否可以访问Admin 站点。 |
is_active |
布尔值。 指示用户的账号是否激活。 它不是用来控制用户是否能够登录,而是描述一种帐号的使用状态。 |
is_superuser |
是否是超级用户。超级用户具有所有权限。 |
last_login |
用户最后一次登录的时间。 |
date_joined |
账户创建的时间。 当账号创建时,默认设置为当前的date/time。 |
上面缺少一些字段,所以后面我们会对当前内置的用户模型进行改造,比如说它里面没有手机号字段,后面我们需要加上。
(2)重要方法
Django 用户认证(Auth)组件需要导入 auth 模块
|
|
(1)用户对象
|
|
(2)认证方法
|
|
(3)登录和注销方法
|
|
(4)request.user
|
|
(5)自定义用户表
from django.contrib.auth.models import AbstractUser
设置Auth认证模块使用的用户模型为我们自己定义的用户模型
格式:“子应用目录名.模型类名”
AUTH_USER_MODEL = ‘users.User’
8.3、Django的分页器
1
from django.core.paginator import Paginator
(1) index视图
|
|
(2) index.html
|
|
8.4、FBV与CBV
1 FBV :function based view
2 BCV:class based view
8.4.1、前后端分离模式
在开发Web应用中,有两种应用模式:
- 前后端不分离[客户端看到的内容和所有界面效果都是由服务端提供出来的。]
- 前后端分离【把前端的界面效果(html,css,js分离到另一个服务端,python服务端只需要返回数据即可)】
前端形成一个独立的网站,服务端构成一个独立的网站
8.4.2、api接口
应用程序编程接口(Application Programming Interface,API接口),就是应用程序对外提供了一个操作数据的入口,这个入口可以是一个函数或类方法,也可以是一个url地址或者一个网络地址。当客户端调用这个入口,应用程序则会执行对应代码操作,给客户端完成相对应的功能。
当然,api接口在工作中是比较常见的开发内容,有时候,我们会调用其他人编写的api接口,有时候,我们也需要提供api接口给其他人操作。由此就会带来一个问题,api接口往往都是一个函数、类方法、或者url或其他网络地址,不断是哪一种,当api接口编写过程中,我们都要考虑一个问题就是这个接口应该怎么编写?接口怎么写的更加容易维护和清晰,这就需要大家在调用或者编写api接口的时候要有一个明确的编写规范!!!
为了在团队内部形成共识、防止个人习惯差异引起的混乱,我们都需要找到一种大家都觉得很好的接口实现规范,而且这种规范能够让后端写的接口,用途一目了然,减少客户端和服务端双方之间的合作成本。
目前市面上大部分公司开发人员使用的接口实现规范主要有:restful、RPC。
RPC( Remote Procedure Call ): 翻译成中文:远程过程调用[远程服务调用]. 从字面上理解就是访问/调用远程服务端提供的api接口。这种接口一般以服务或者过程式代码提供。
-
服务端提供一个唯一的访问入口地址:http://api.xxx.com/ 或 http://www.xx.com/api 或者基于其他协议的地址
-
客户端请求服务端的时候,所有的操作都理解为动作(action),一般web开发时,对应的就是HTTP请求的post请求
-
通过请求体参数,指定要调用的接口名称和接口所需的参数
action=get_all_student&class=301&sex=1
m=get_all_student&sex=1&age=22
command=100&sex=1&age=22
rpc接口多了,对应函数名和参数就多了,前端在请求api接口时难找.对于年代久远的rpc服务端的代码也容易出现重复的接口
restful: 翻译成中文: 资源状态转换.(表征性状态转移)
-
把服务端提供的所有的数据/文件都看成资源, 那么通过api接口请求数据的操作,本质上来说就是对资源的操作了.
因此,restful中要求,我们把当前接口对外提供哪种资源进行操作,就把资源的名称写在url地址。
-
web开发中操作资源,最常见的最通用的无非就是增删查改,所以restful要求在地址栏中声明要操作的资源是什么。然后通过http请求动词来说明对该资源进行哪一种操作.
POST http://www.xxx.com/api/students/ 添加学生数据
GET http://www.xxx.com/api/students/ 获取所有学生
GET http://www.xxx.com/api/students/1/ 获取id=pk的学生
DELETE http://www.xxx.com/api/students/1/ 删除id=pk的一个学生
PUT http://www.xxx.com/api/students/1/ 修改一个学生的全部信息 [id,name,sex,age,]
PATCH http://www.xxx.com/api/students/1/ 修改一个学生的部分信息[age]
也就是说,我们仅需要通过url地址上的资源名称结合HTTP请求动作,就可以说明当前api接口的功能是什么了。restful是以资源为主的api接口规范,体现在地址上就是资源就是以名词表达。rpc则以动作为主的api接口规范,体现在接口名称上往往附带操作数据的动作。
8.4.3、RESTful API规范
REST全称是Representational State Transfer,中文意思是表述(编者注:通常译为表征)性状态转移。 它首次出现在2000年Roy Fielding的博士论文中。
RESTful是一种专门为Web 开发而定义API接口的设计风格,尤其适用于前后端分离的应用模式中。
这种风格的理念认为后端开发任务就是提供数据的,对外提供的是数据资源的访问接口,所以在定义接口时,客户端访问的URL路径就表示这种要操作的数据资源。
而对于数据资源分别使用POST、DELETE、GET、UPDATE等请求动作来表达对数据的增删查改。
GET | /students | 获取所有学生 |
---|---|---|
请求方法 | 请求地址 | 后端操作 |
POST | /students | 增加学生 |
GET | /students/ | 获取编号为pk的学生 |
PUT | /students/ | 修改编号为pk的学生 |
DELETE | /students/ | 删除编号为pk的学生 |
restful规范是一种通用的规范,不限制语言和开发框架的使用。事实上,我们可以使用任何一门语言,任何一个框架都可以实现符合restful规范的API接口。
参考文档:http://www.runoob.com/w3cnote/restful-architecture.html
接口实现过程中,会存在幂等性。所谓幂等性是指代客户端发起多次同样请求时,是否对于服务端里面的资源产生不同结果。如果多次请求,服务端结果还是一样,则属于幂等接口,如果多次请求,服务端产生结果是不一样的,则属于非幂等接口。
请求方式 | 是否幂等 | 是否安全 |
---|---|---|
GET | 幂等 | 安全 |
POST | 不幂等 | 不安全 |
PUT/PATCH | 幂等 | 不安全 |
DELETE | 幂等 | 不安全 |
8.4.4、CBV使用
之前我们用的视图函数叫FBV(也就是函数型视图函数),这里我们来试试CBV(类视图函数)的写法。类视图函数可以让代码看起来更简洁,用起来更方便。
|
|
# FBV模式
# path('index/', views.index),
# CBV模式
path("index/",views.IndexView.as_view()),
path("books/",views.BookView.as_view())
8.5、csrftoken(跨站请求伪造)
CSRF(Cross-Site Request Forgery,跨站点伪造请求)是一种网络攻击方式,该攻击可以在受害者毫不知情的情况下以受害者名义伪造请求发送给受攻击站点,从而在未授权的情况下执行在权限保护之下的操作,具有很大的危害性。具体来讲,可以这样理解CSRF攻击:攻击者盗用了你的身份,以你的名义发送恶意请求,对服务器来说这个请求是完全合法的,但是却完成了攻击者所期望的一个操作,比如以你的名义发送邮件、发消息,盗取你的账号,添加系统管理员,甚至于购买商品、虚拟货币转账等。
-- 企业邮箱
-- pornhub.com
token其实就是一个令牌,用于用户验证的,token的诞生离不开CSRF。正是由于上面的Cookie/Session的状态保持方式会出现CSRF,所以才有了token。
- 解除中间件注释
- 无csrf_token数据的post请求
8.5.1、基本使用
一、form表单提交
在html页面form表单中直接添加{% csrf_token%}
![屏幕快照 2021-10-31 下午10.30.29](assets/屏幕快照 2021-10-31 下午10.30.29.png)
二、ajax提交
方式1:放在请求数据中。
|
|
方式2:放在请求头
|
|
8.5.2、全局使用,局部禁csrf
(1) 在视图函数上加装饰器
from django.views.decorators.csrf import csrf_exempt,csrf_protect
@csrf_exempt
def 函数名(request): # 加上装饰器后,这个视图函数,就没有csrf校验了
(2) 视图类
|
|
8.6、ORM进阶
8.6.1、queryset特性
(1)可切片
使用Python 的切片语法来限制查询集
记录的数目 。它等同于SQL 的LIMIT
和OFFSET
子句。
|
|
不支持负的索引(例如Article.objects.all()[-1]
)。通常,查询集
的切片返回一个新的查询集
—— 它不会执行查询。
(2)可迭代
|
|
(3)惰性查询
查询集
是惰性执行的 —— 创建查询集
不会带来任何数据库的访问。你可以将过滤器保持一整天,直到查询集
需要求值时,Django 才会真正运行这个查询。
|
|
一般来说,只有在“请求”查询集
的结果时才会到数据库中去获取它们。当你确实需要结果时,查询集
通过访问数据库来求值。 关于求值发生的准确时间。
(4)缓存机制
每个查询集
都包含一个缓存来最小化对数据库的访问。理解它是如何工作的将让你编写最高效的代码。
在一个新创建的查询集
中,缓存为空。首次对查询集
进行求值 —— 同时发生数据库查询 ——Django 将保存查询的结果到查询集
的缓存中并返回明确请求的结果(例如,如果正在迭代查询集
,则返回下一个结果)。接下来对该查询集
的求值将重用缓存的结果。
请牢记这个缓存行为,因为对查询集
使用不当的话,它会坑你的。例如,下面的语句创建两个查询集
,对它们求值,然后扔掉它们:
|
|
注:简单地打印查询集不会填充缓存。
这意味着相同的数据库查询将执行两次,显然倍增了你的数据库负载。同时,还有可能两个结果列表并不包含相同的数据库记录,因为在两次请求期间有可能有Article被添加进来或删除掉。为了避免这个问题,只需保存查询集
并重新使用它:
|
|
何时查询集会被缓存?
- 遍历queryset时
- if语句(为了避免这个,可以用exists()方法来检查是否有数据)
所以单独queryset的索引或者切片都不会缓存。
|
|
(5)exists()与iterator()方法
exists
简单的使用if语句进行判断也会完全执行整个queryset并且把数据放入cache,虽然你并不需要这些 数据!为了避免这个,可以用exists()方法来检查是否有数据:
|
|
iterator
当queryset非常巨大时,cache会成为问题。
处理成千上万的记录时,将它们一次装入内存是很浪费的。更糟糕的是,巨大的queryset可能会锁住系统 进程,让你的程序濒临崩溃。要避免在遍历数据的同时产生queryset cache,可以使用iterator()方法 来获取数据,处理完数据就将其丢弃。
|
|
当然,使用iterator()方法来防止生成cache,意味着遍历同一个queryset时会重复执行查询。所以使 #用iterator()的时候要当心,确保你的代码在操作一个大的queryset时没有重复执行查询。
queryset的cache是用于减少程序对数据库的查询,在通常的使用下会保证只有在需要的时候才会查询数据库。 使用exists()和iterator()方法可以优化程序对内存的使用。不过,由于它们并不会生成queryset cache,可能 会造成额外的数据库查询。
总结:在使用缓存机制还是生成器机制的选择上如果是,数据量大情况主要使用生成器;数据少使用次数多的情况使用缓存机制。
8.6.2、中介模型
处理类似搭配 pizza 和 topping 这样简单的多对多关系时,使用标准的ManyToManyField
就可以了。但是,有时你可能需要关联数据到两个模型之间的关系上。
例如,有这样一个应用,它记录音乐家所属的音乐小组。我们可以用一个ManyToManyField
表示小组和成员之间的多对多关系。但是,有时你可能想知道更多成员关系的细节,比如成员是何时加入小组的。
对于这些情况,Django 允许你指定一个中介模型来定义多对多关系。 你可以将其他字段放在中介模型里面。源模型的ManyToManyField
字段将使用through
参数指向中介模型。对于上面的音乐小组的例子,代码如下:
|
|
既然你已经设置好ManyToManyField
来使用中介模型(在这个例子中就是Membership
),接下来你要开始创建多对多关系。你要做的就是创建中介模型的实例:
|
|
与普通的多对多字段不同,你不能使用add
、 create
和赋值语句(比如,beatles.members = [...]
)来创建关系:
|
|
为什么不能这样做? 这是因为你不能只创建 Person
和 Group
之间的关联关系,你还要指定 Membership
模型中所需要的所有信息;而简单的add
、create
和赋值语句是做不到这一点的。所以它们不能在使用中介模型的多对多关系中使用。此时,唯一的办法就是创建中介模型的实例。
remove()
方法被禁用也是出于同样的原因。但是clear()
方法却是可用的。它可以清空某个实例所有的多对多关系:
|
|
8.6.3、数据库表反向生成模型类
众所周知,Django较为适合原生开发,即通过该框架搭建一个全新的项目,通过在修改models.py来创建新的数据库表。但是往往有时候,我们需要利用到之前的已经设计好的数据库,数据库中提供了设计好的多种表单。那么这时如果我们再通过models.py再来设计就会浪费很多的时间。所幸Django为我们提供了inspecdb的方法。他的作用即使根据已经存在对的mysql数据库表来反向映射结构到models.py中.
我们在展示django ORM反向生成之前,我们先说一下怎么样正向生成代码。
正向生成,指的是先创建model.py文件,然后通过django内置的编译器,在数据库如mysql中创建出符合model.py的表。
反向生成,指的是先在数据库中create table,然后通过django内置的编译器,生成model代码。
|
|
8.6.4、查询优化
(1)exists()
判断查询集中是否有数据,如果有则返回True,没有则返回False
(2)select_related()
对于一对一字段(OneToOneField)和外键字段(ForeignKey),可以使用select_related 来对QuerySet进行优化。
select_related 返回一个QuerySet
,当执行它的查询时它沿着外键关系查询关联的对象的数据。它会生成一个复杂的查询并引起性能的损耗,但是在以后使用外键关系时将不需要数据库查询。
简单说,在对QuerySet使用select_related()函数后,Django会获取相应外键对应的对象,从而在之后需要的时候不必再查询数据库了。
下面的例子解释了普通查询和select_related()
查询的区别。
查询id=2的的书籍的出版社名称,下面是一个标准的查询:
|
|
如果我们使用select_related()函数:
|
|
多外键查询
这是针对publish的外键查询,如果是另外一个外键呢?让我们一起看下:
|
|
观察logging结果,发现依然需要查询两次,所以需要改为:
|
|
或者:
|
|
(3)prefetch_related()
对于多对多字段(ManyToManyField)和一对多字段,可以使用prefetch_related()来进行优化。
prefetch_related()和select_related()的设计目的很相似,都是为了减少SQL查询的数量,但是实现的方式不一样。后者是通过JOIN语句,在SQL查询内解决问题。但是对于多对多关系,使用SQL语句解决就显得有些不太明智,因为JOIN得到的表将会很长,会导致SQL语句运行时间的增加和内存占用的增加。若有n个对象,每个对象的多对多字段对应Mi条,就会生成Σ(n)Mi 行的结果表。
prefetch_related()的解决方法是,分别查询每个表,然后用Python处理他们之间的关系。
|
|
改为prefetch_related:
|
|
(4)extra
|
|
有些情况下,Django的查询语法难以简单的表达复杂的 WHERE
子句,对于这种情况, Django 提供了 extra()
QuerySet
修改机制 — 它能在 QuerySet
生成的SQL从句中注入新子句
extra可以指定一个或多个 参数
,例如 select
, where
or tables
. 这些参数都不是必须的,但是你至少要使用一个!要注意这些额外的方式对不同的数据库引擎可能存在移植性问题.(因为你在显式的书写SQL语句),除非万不得已,尽量避免这样。
参数之select
The select
参数可以让你在 SELECT
从句中添加其他字段信息,它应该是一个字典,存放着属性名到 SQL 从句的映射。
queryResult=models.Article
.objects.extra(select={'is_recent': "create_time > '2017-09-05'"})
结果集中每个 Entry 对象都有一个额外的属性is_recent, 它是一个布尔值,表示 Article对象的create_time 是否晚于2017-09-05.
参数之where
/ tables
您可以使用where
定义显式SQL WHERE
子句 - 也许执行非显式连接。您可以使用tables
手动将表添加到SQL FROM
子句。
where
和tables
都接受字符串列表。所有where
参数均为“与”任何其他搜索条件。
举例来讲:
|
|
8.7、上传文件
8.7.1、form表单上传文件
|
|
|
|
8.7.2、Ajax(基于FormData)
FormData是什么呢?
XMLHttpRequest Level 2添加了一个新的接口FormData
.利用FormData对象
,我们可以通过JavaScript用一些键值对来模拟一系列表单控件,我们还可以使用XMLHttpRequest的send()
方法来异步的提交这个"表单".比起普通的ajax,使用FormData
的最大优点就是我们可以异步上传一个二进制文件.
所有主流浏览器的较新版本都已经支持这个对象了,比如Chrome 7+、Firefox 4+、IE 10+、Opera 12+、Safari 5+。
|
|
|
|
8.7.3、ImageField 和 FileField
ImageField 和 FileField 可以分别对图片和文件进行上传到指定的文件夹中。
- 在下面的 models.py 中 :
picture = models.ImageField(upload_to=‘avatars/’, default=“avatars/default.png”,blank=True, null=True) 注:定义 ImageField 字段时必须制定参数 upload_to这个字段要写相对路径,
这个参数会加在 settings.py 中的 MEDIA_ROOT后面, 形成一个路径, 这个路径就是上 传图片的存放位置,默认在Django项目根路径下,也就是MEDIA_ROOT默认是Django根目录
所以要先设置好 mysite/settings.py中的 settings.py 中的 MEDIA_ROOT
|
|
|
|
Django会在项目的根目录创建avatars文件夹,将上传文件下载到该文件夹中,avatar字段保存的是文件的相对路径。
- 在 mysite/settings.py中 :
|
|
MEDIA_ROOT:存放 media 的路径, 这个值加上 upload_to的值就是真实存放上传图片文件位置
MEDIA_URL:给这个属性设值之后,静态文件的链接前面会加上这个值,如果设置这个值,则UserInfo.avatar.url自动替换成:/media/avatars/default.png,可以在模板中直接调用:。
3.url.py:
|
|
浏览器可以直接访问http://127.0.0.1:8000/media/yuan/avatars/%E7%86%8A%E7%8C%AB.webp,即我们的用户上传文件。
最后再给大家补充一个用户文件夹路径
|
|
4.FileField 和 ImageFiled 相同。
8.7.4、导入表格批量创建数据
|
|
8.8、websocket的实现
8.8.1、websocket的实现
http请求的特点:
短链接
基于请求响应
基于TCP
无状态保存
WebSocket
是一种在单个TCP
连接上进行全双工通讯的协议。WebSocket
允许服务端主动向客户端推送数据。在WebSocket
协议中,客户端浏览器和服务器只需要完成一次握手就可以创建持久性的连接,并在浏览器和服务器之间进行双向的数据传输。
在一个HTTP访问周期里,如果要执行一个长时间任务,为了避免浏览器等待,后台必须使用异步动作。与此同时也要满足实时需求,用户提交了任务后可以随时去访问任务详情页面,在这里用户能够实时地看到任务的执行进度。
针对异步任务处理,我们使用了Celery把任务放到后台执行。Celery 是一个基于python开发的分布式异步消息任务队列,通过它可以轻松的实现任务的异步处理,关于它的使用方法《网易乐得RDS设计》也有提到。Celery在处理一个任务的时候,会把这个任务的进度记录在数据库中。
实现任务的后台执行后,下一步就要解决实时地更新进度信息到网页的问题。从上一步可以知道,数据库中已经存在了任务的进度信息,网页直接访问数据库就可以拿到数据。但是数据库中的进度信息更新的速率不固定,如果使用间隔时间比较短的ajax轮询来访问数据库,会产生很多无用请求,造成资源浪费。综合考虑,我们决定使用WebSocket来实现推送任务进度信息的功能。网站是使用Django搭建的,原生的MTV(模型-模板-视图)设计模式只支持Http请求。幸好Django开发团队在过去的几年里也看到实时网络应用的蓬勃发展,发布了Channels,以插件的形式为Django带来了实时能力。下面两张图展示了原生Django与集成Channels的Django。
WebSocket的请求头中重要的字段:
Connection和Upgrade:表示客户端发起的WebSocket请求
Sec-WebSocket-Version:客户端所使用的WebSocket协议版本号,服务端会确认是否支持该版本号
Sec-WebSocket-Key:一个Base64编码值,由浏览器随机生成,用于升级request WebSocket的响应头中重要的字段
HTTP/1.1 101 Swi tching Protocols:切换协议,WebSocket协议通过HTTP协议来建立运输层的TCP连接
Connection和Upgrade:表示服务端发起的WebSocket响应 Sec-WebSocket-Accept:表示服务器接受了客户端的请求,由Sec-WebSocket-Key计算得来
WebSocket协议的优点:
支持双向通信,实时性更强 数据格式比较轻量,性能开销小,通信高效 支持扩展,用户可以扩展协议或者实现自定义的子协议(比如支持自定义压缩算法等)
WebSocket协议的缺点:
少部分浏览器不支持,浏览器支持的程度与方式有区别 长连接对后端处理业务的代码稳定性要求更高,后端推送功能相对复杂 成熟的HTTP生态下有大量的组件可以复用,WebSocket较少
WebSocket的应用场景:
即时聊天通信,网站消息通知 在线协同编辑,如腾讯文档 多玩家在线游戏,视频弹幕,股票基金实施报价
8.8.2、Channels组件
Channels为Django带来了一些新特性,最明显的就是添加了对WebSocket的支持。Channels最重要的部分也可以看做是任务队列,消息被生产者推到通道,然后传递给监听通道的消费者之一。它与传统的任务队列的主要的区别在于Channels通过网络工作,使生产者和消费者透明地运行在多台机器上,这个网络层就叫做channel layer。Channels推荐使用redis作为它的channel layer backend,但也可以使用其它类型的工具,例如rabbitmq、内存或者IPC。关于Channels的一些基本概念,推荐阅读官方文档。
继承WebSocketConsumer
的连接。
AuthMiddlewareStack:用于WebSocket认证,继承了Cookie Middleware,SessionMiddleware,SessionMiddleware。django的channels封装了django的auth模块,使用这个配置我们就可以在consumer中通过下边的代码获取到用户的信息
|
|
self.scope类似于django中的request,包含了请求的type、path、header、cookie、session、user等等有用的信息
配置和使用
channels==2.1.3
channels-redis==2.3.0。
启动 Redis 服务默认使用 6379 端口,Django 将使用该端口连接 Redis 服务。
更新项目配置文件 settings.py 中的 INSTALLED_APPS 项
|
|
在 wsgi.py
同级目录新增文件 routing.py
,其作用类型与 urls.py
,用于分发webscoket
请求:
from django.urls import path
from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter
from table.consumers import TableConsumer
application = ProtocolTypeRouter({
# Empty for now (http->django views is added by default)
'websocket': AuthMiddlewareStack(
URLRouter([
path('ws/table/<slug:table_id>/', TableConsumer),
])
),
})
routing.py
路由文件跟django
的url.py
功能类似,语法也一样,意思就是访问ws/table/
都交给TableConsumer处理。
新增 app 名为 table
,在 table
目录下新增 consumers.py
:
from channels.generic.websocket import AsyncJsonWebsocketConsumer
class TableConsumer(AsyncJsonWebsocketConsumer):
table = None
async def connect(self):
self.table = 'table_{}'.format(self.scope['url_route']['kwargs']['table_id'])
# Join room group
await self.channel_layer.group_add(self.table, self.channel_name)
await self.accept()
async def disconnect(self, close_code):
# Leave room group
await self.channel_layer.group_discard(self.table, self.channel_name)
# Receive message from WebSocket
async def receive_json(self, content, **kwargs):
# Send message to room group
self.from_client = str(self.scope["client"])
await self.channel_layer.group_send(self.table, {'type': 'message', 'message': content,"from_client":self.from_client})
# Receive message from room group
async def message(self, event):
message = event['message']
print("self.scope",self.scope)
print("event",event)
print("message",message)
# Send message to WebSocket
print(":::",event["from_client"]+">>>"+message)
await self.send_json(event["from_client"]+">>>"+message)
TableConsumer
类中的函数依次用于处理连接、断开连接、接收消息和处理对应类型的消息,其中channel_layer.group_send(self.table, {'type': 'message', 'message': content})
方法,self.table
参数为当前组的组id, {'type': 'message', 'message': content}
部分分为两部分,type
用于指定该消息的类型,根据消息类型调用不同的函数去处理消息,而 message
内为消息主体。
在 table
目录下的 views.py
中新增函数:
|
|
|
|
前端实现WebSocket
WebSocket对象一个支持四个消息:onopen,onmessage,oncluse和onerror,我们这里用了两个onmessage和onclose
onopen: 当浏览器和websocket服务端连接成功后会触发onopen消息
onerror: 如果连接失败,或者发送、接收数据失败,或者数据处理出错都会触发onerror消息
onmessage: 当浏览器接收到websocket服务器发送过来的数据时,就会触发onmessage消息,参数e包含了服务端发送过来的数据
onclose: 当浏览器接收到websocket服务器发送过来的关闭连接请求时,会触发onclose消息载请注明出处。
在 table
的 templates\table
目录下新增 table.html
:
|
|
九、Django3的ASGI
9.1、Web应用程序和web服务器
Web应用程序(Web)是一种能完成web业务逻辑,能让用户基于web浏览器访问的应用程序,它可以是一个实现http请求和响应功能的函数或者类,也可以是Django、Flask、sanic等这样的web框架,当然也可以是其他语言的web程序或web框架。
Web服务器(Web Server)是一种运行于网站后台(物理服务器)的软件。Web服务器主要用于提供网页浏览或文件下载服务,它可以向浏览器等Web客户端提供html网页文档,也可以提供其他类型的可展示文档,让客户端用户浏览;还可以提供数据文件下载等。目前世界上最主流的Web服务器有 Nginx 、Apache、IIS、tomcat。
|
|
9.2、网关接口
网关接口(Gateway Interface,GI)就是一种为了实现加载动态脚本而运行在Web服务器和Web应用程序中的通信接口,也可以理解为一份协议/规范。只有Web服务器和Web应用程序都实现了网关接口规范以后,双方的通信才能顺利完成。常见的网关接口协议:CGI,FastCGI,WSGI,ASGI。
(1)CGI
CGI(Common Gateway Inteface): 字面意思就是通用网关接口,
它是外部应用程序与Web服务器之间的接口标准
意思就是它用来规定一个程序该如何与web服务器程序之间通信从而可以让这个程序跑在web服务器上。当然,CGI 只是一个很基本的协议,在现代常见的服务器结构中基本已经没有了它的身影,更多的则是它的扩展和更新。
FastCGI: CGI的一个扩展, 提升了性能,废除了 CGI fork-and-execute (来一个请求 fork 一个新进程处理,处理完再把进程 kill 掉)的工作方式,转而使用一种长生存期的方法,减少了进程消耗,提升了性能。
这里 FastCGI 就应用于前端 server(nginx)与后端 server(uWSGI)的通信中,制定规范等等,让前后端服务器可以顺利理解双方都在说什么(当然 uWSGI 本身并不用 FastCGI, 它有另外的协议)
(2)WSGI
WSGI(Python Web Server GateWay Interface):它是用在 python web 框架编写的应用程序与后端服务器之间的规范(本例就是 Django 和 uWSGI 之间),让你写的应用程序可以与后端服务器顺利通信。在 WSGI 出现之前你不得不专门为某个后端服务器而写特定的 API,并且无法更换后端服务器,而 WSGI 就是一种统一规范, 所有使用 WSGI 的服务器都可以运行使用 WSGI 规范的 web 框架,反之亦然。
WSGI和ASGI,都是基于Python设计的网关接口(Gateway Interface,GI)。
Web服务器网关接口(Python Web Server Gateway Interface,WSGI),是Python为了解决Web服务器端与客户端之间的通信基于CGI标准而设计的。实现了WSGI协议的web服务器有:uWSGI、uvicorn、gunicorn。像django框架一般开发中就不会使用runserver来运行,而是采用上面实现了WSGI协议的web服务器来运行。
django中运行runserver命令时,其实内部就启动了wsgiref模块作为web服务器运行的。wsgiref是python内置的一个简单地遵循了wsgi接口规范的web服务器程序。
|
|
开发中,我们一般使用uWSGI或者Gunicorn作为web服务器运行django。
(3)uWSGI
uWSGI 是一个快速的,自我驱动的,对开发者和系统管理员友好的应用容器服务器,用于接收前端服务器转发的动态请求并处理后发给 web 应用程序。完全由 C 编写,实现了WSGI协议,uwsgi,http等协议。注意:uwsgi 协议是一个 uWSGI服务器自有的协议,用于定义传输信息的类型,常用于uWSGI服务器与其他网络服务器的数据通信中。
uwsgi: 是uWSGI服务器实现的独有的协议, 网上没有明确的说明这个协议是用在哪里的,我个人认为它是用于前端服务器与 uwsgi 的通信规范,相当于 FastCGI的作用。
9.3、关于ASGI
9.3.1、历史趣谈
2019 年 12 月,Django3.0 发布,它具有一个有趣的新特性——支持 ASGI 服务器。
2003 年,诸如Zope、Quixote之类的各式各样的 Python Web 框架都附带了自己的 Web 服务器,或者拥有自己的本地接口来与流行的网络服务器(如 Apache)进行通信。
成为一名 Python Web 开发人员意味着要全身心投入到一个完整的技术栈中,但是如果需要另一个框架时,则必须重新学习所有内容。可以想象到这会导致碎片化。PEP 333,即 Python Web 服务器网关接口 v1.0,尝试通过定义一个被称为 WSGI(Web Server Gateway Interface)的简单标准接口来解决这个问题。它的卓越之处在于它的简单性。
WSGI 如此流行,以至它不仅被诸如 Django 和 Pylons 之类的大型 Web 框架所采用,还被诸如 Bottle 之类的微框架所采用。
为什么有ASGI的出现
如果我们对 WSGI 非常满意的话,为什么还要提出 ASGI 呢?如果仔细检查网络请求的整个处理流程,那么答案就非常明显了。您可以查看 在Django中网络请求如何工作 的动画。请注意,在数据库查询之后且响应发送之前,框架是如何等待的。这就是同步处理的缺点。
坦白说,直到 2009 年 Node.js 出现时,这种缺陷才变得明显或紧迫。Node.js 的创建者 Ryan Dahl 受到 C10K 问题的困扰,即为什么像 Apache 这样的流行 Web 服务器无法处理 10,000 个或更多的并发连接(给定典型的 Web 服务器硬件,内存将会耗尽)。他思考到 “查询数据库时,软件在做什么?”。
当然,答案是什么也没有发生。它正在等待数据库的响应。Ryan 认为网络服务器根本不应该去等待 I / O 活动。换言之,它应该切换为处理其他请求,并在较慢的活动完成时能得到通知。
越来越明显的是,基于异步事件的架构是解决多种并发问题的正确方法。也许这就是为什么 Python 的创建者 Guido 亲自为 Tulip 项目提供语言级别支持的原因,这个项目后来成为了 asyncio 模块。最终,Python 3.7 添加了新的关键字 async 和 await ,用于支持异步事件循环。这不仅对如何编写 Python 代码而且对代码执行也具有重大意义。
Python 的两个世界
虽然使用 Python 编写异步代码非常简单,在函数定义前添加 async 关键字,但是您必须非常小心,不要打破一个重要的规则:不要随意混合使用同步代码和异步代码。
这是因为同步代码可以阻止异步代码中的事件循环。这种情况会使您的应用程序陷入停顿。正如 Andrew Goodwin 所写:这会将您的代码分为两个世界——具有不同库和调用风格的“同步代码”和“异步代码”。
回到 WSGI,这意味着我们无法编写异步可调用对象并将其嵌入。WSGI 是为同步世界编写的。我们将需要一种新的机制来调用异步代码。但是,如果每个人都编写自己的一套机制,我们将回到最初的不兼容地狱。因此,对于异步代码,我们需要一个类似于 WSGI 的新标准。因此,ASGI 诞生了。
ASGI 还有其他一些目的。但是在此之前,让我们看一下两种类似的 Web 应用程序“Hello World”,它们分别采用 WSGI 和 ASGI 风格。
与 WSGI 一样,ASGI 可调用对象可以一个接一个地链接在一起,以处理 Web 请求(以及其他协议请求),即链式调用。实际上,ASGI 是 WSGI 的超集且可以调用 WSGI 可调用对象。ASGI 还支持长时间轮询,慢速流媒体和其他响应类型,而无需侧向加载,从而可以加快响应速度。
因此,ASGI 引入了构建异步 Web 界面和处理双向协议的新方式。客户端或服务器端都无需等待对方进行通信——这可以随时异步发生。现存且以同步代码编写的基于 WSGI 的 Web 框架将不支持这种事件驱动的工作方式。
Django 演变
同时,想将所有这些异步优势带给 Django 面临一个重大的问题 —— 所有 Django 代码都是以同步风格编写的。如果我们需要编写任何异步代码,那么需要一个采用异步风格编写的整个 Django 框架的副本。换句话说,创建两个 Django 世界。
好吧,不要惊慌——我们可能不必编写一个完整的副本,因为有聪明的方式可以在两个世界之间重用一些代码。正如领导 Django Async 项目的安德鲁·戈德温(Andrew Godwin)正确地指出的那样,“这是 Django 历史上最大规模的修订之一”。一个雄心勃勃的项目采用异步风格重新实现 ORM、请求处理程序(request handler)、模板渲染器(Template renderer)等组件。这将分阶段进行,并在多个版本中完成。如下是安德鲁的预想(这不视为已提交的时间表):
- Django 3.0-ASGI 服务器
- Django 3.1-异步视图
- Django 3.2 / 4.0-异步 ORM
您可能正在考虑其余的组件,例如模板渲染、表单和缓存等。它们可能仍然保持同步或者其异步实现会纳入到将来的技术路线中。但是以上是 Django 在异步世界中发展的关键里程碑。
9.3.2、asgi的使用
ASGI,是构建于WSGI接口规范之上的异步服务器网关接口,是WSGI的延伸和扩展。
A指的是Async,异步的意思。
协议,规范 | 支持的请求协议(常见,未列全) | 同步/异步 | 支持的框架 |
---|---|---|---|
CGI | HTTP | CGI程序 | |
WSGI | HTTP | 同步 | django3.0以前,Flask1.0 |
ASGI | HTTP,HTTP2,WebSocket等 | 同步/异步 | FastAPI,Quart,Sanic,Tornado,django3.0以后,flask2.0 |
在 Python3.5 之后增加 async/await 特性之后简化了协程操作以后,异步编程变得异常火爆,越来越多开发者投入异步的怀抱。
3.0版本以前,django所提供的所有内部功能都是基于同步编程的。所以,在以往django开发中,针对网络请求,数据库读取等IO操作形成的阻塞,往往会导致项目运行性能的下降。虽然等待I/O操作数微秒时,但是随着流量的增加和操作的频率上升,这一点点的阻塞就会导致整个项目运作的缓慢。而如果换成异步就不会有任何阻塞,还可以同时处理其他任务,从而以较低的延迟处理更多的请求。所以在目前python开发中,越来越多的框架开始支持了异步编程。所以,3.0版本以后,django开始支持异步编程,可以让开发者在django中使用python第三方异步模块,推出了asgi异步web服务器。3.1版本推出了异步视图,当然,目前django的异步编程还不够完善,django中只有极少的功能是支持了异步操作。
Uvicorn 是一个快速的 ASGI 服务器,Uvicorn 是基于 uvloop 和 httptools 构建的,是 Python 异步生态中重要的一员。
Uvicorn 当前支持 HTTP / 1.1 和 WebSockets,将来计划支持HTTP / 2。
文档:https://www.uvicorn.org/
安装uvicorn
|
|
项目根目录下,运行django项目
|
|
开发中一般使用gunicorn来管理uvicorn。所以可以一并安装
|
|
运行
|
|
- 原文作者:Yuan
- 原文链接:http://www.yuan316.com/post/Django3.2%E6%A1%86%E6%9E%B6/
- 版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议进行许可,非商业转载请注明出处(作者,原文链接),商业转载请联系作者获得授权。