orm查询优化
惰性查询, 目的: 减少不必要的数据库操作, 降低数据库的压力
缓存数据库, redis, mongodb, 目的: 缓解主库压力, 相当于一个非常大的内存, 速度快同时也能做持久化
能少走一次数据库就少走一次
only与defer
only:
- only括号内放字段, 查询结果是一个列表套数据对象
- "对象.括号内的字段"不会再查询数据库, 直接获取属性值
- "对象.括号内没有的字段"会查询数据库获取结果
queryset = models.Book.objects.only('title')
# 查询1次数据库: SELECT "..."."id", "..."."title" FROM "..." ...
print(queryset) # <QuerySet [<Book: Book object>, ...]>,
for obj in queryset: # 查询1次数据库: SELECT "..."."id", "..."."title" FROM "..." ...
print(obj.title) # 不查询数据库: 红楼梦, ...
# 一共查询7次数据库: SELECT "..."."id", "..."."price" FROM "..." WHERE "..."."id" = 1; ...
print(obj.price) # 111.00, ...
defer:
- defer与only互为反操作
- "对象.括号内的字段"会查询数据库获取结果
- "对象.括号内没有的字段"不会再查询数据库, 直接获取属性值
queryset = models.Book.objects.defer('title')
for obj in queryset: # 查询1次数据库: SELECT "..."."id", "..."."price", "..."."publish_date", "..."."publish_id" FROM "...";
# 一共查7次数据库: SELECT "..."."id", "..."."title" FROM "..." WHERE "..."."id" = 1;
print(obj.title) # 红楼梦, ...
print(obj.price) # 不查询数据库: 111.00, ...
selected_related与prefetch_related
selected_related:
- select_related括号内只能放外键字段,
- 并且外键字段只能是多对一或者一对一, 不能是多对多,
- 内部是自动联表操作, 会将括号中的外键字段所关联的表与当前表自动拼接成一张表,
- 然后将表中的一条条数据封装成一个个对象, 使跨表查询也不需要重复走数据库,
- 括号中可以放多个外键字段, 会将全部外键字段关联的表与当前表拼成一张大表
queryset1 = models.Book.objects.all()
# 查询1次数据库: SELECT "x"."id", "x"."title", "x"."price", "x"."publish_date", "x"."publish_id" FROM "x" ...
print(queryset1) # <QuerySet [<Book: Book object>, ...]>
for obj in queryset1: # 查询1次数据库: SELECT * FROM "app01_book" ...
# 一共查询7次数据库: SELECT "app01_publish"."id", "y"."name", "y"."address" FROM "y" WHERE "y"."id" = 1; ...
print(obj.publish.name) # 如果改成: print(obj.title), 则不查询数据库
queryset2 = models.Book.objects.select_related('publish')
# 查1次数据库: SELECT "x".*, "x".* FROM "x" INNER JOIN "y" ON ("x"."publish_id" = "y"."id"); ...
print(queryset2) # <QuerySet [<Book: Book object>, ...]>
for obj in queryset2: # 查询1次数据库: SELECT "x".*, "x".* FROM "x" INNER JOIN "y" ON ("x"."publish_id" = "y"."id"); ...
print(obj.title) # 不查询数据库: 红楼梦, ...
print(obj.publish.name) # 不查询数据库: 东方出版社, ...
prefetch_related:
- 内部是子查询, 按步骤查询多张表, 然后将查询的结果封装到对象中, 给用户的感觉像是联表操作
- 括号中可以放多个外键字段(包括多对多关系), 每多放一个外键字段, 就会多走一条查询该外键字段关联表的sql语句
queryset = models.Book.objects.prefetch_related('publish', 'author')
'''
查询3次数据库:
1. SELECT "x"."id", "x"."title", "x"."price", "x"."publish_date", "x"."publish_id" FROM "x" ...
2. SELECT "y"."id", "y"."name", "y"."address" FROM "y" WHERE "y"."id" IN (1, 2, ...); ...
3. SELECT ..., "z".* FROM "z" INNER JOIN "w" ON ("z"."id" = "w"."author_id") WHERE "w"."book_id" IN (1, 2, ...); ...
'''
print(queryset) # <QuerySet [<Book: Book object>, ...]>
for obj1 in queryset: # 查询3次数据库, sql语句同上
print(obj1.publish.name) # 不查询数据库: 东方出版社, ...
# print(obj1.author.all().values_list('name')) # <QuerySet [('jason',), ...]>, queryset对象获取字段值仍然会7次走数据库
for obj2 in obj1.author.all():
print(obj1.title, obj2.name) # 不查询数据库: 红楼梦 jason, ...
小结
selected_related: 耗时在数据库层面的联表操作上
prefetch_related: 耗时在查询的次数上
当两张表特别大时, selected_related花费时间可能更多
MTV与MCV模型
django自称为MTV框架, 本质还是MVC
MTV:
- M: models, 数据库模型表
- T: templates, html模板
- V: views, 视图层(FBV | CBV)
MVC:
- Model, 数据库模型
- View: 处理数据的显示
- Controller: 处理数据的交互
choices参数
一般用于性别, 学历等能完全列举出可能值的字段,
例如: 数据在数据库中按数字存, 取的时候按照提前设定的对应关系转换成真正的数据
migrations文件夹下必须要有一个__init__.py
文件, 否则会报错: No changes detected
# models.py
class User(models.Model):
...
gender_choice = (
(1, '男'),
(2, '女'),
(3, '其他'),
)
gender = models.IntegerField(choices=gender_choice)
#test.py
user_obj1 = models.User.objects.get(pk=3)
user_obj2 = models.User.objects.get(pk=10)
print(user_obj1) # User object
print(user_obj1.get_gender_display()) # 其他
print(user_obj2.get_gender_display()) # 4
'''
1. 包含choices参数的字段类型获取对应关系值的句式: get_字段名_display()
2. 弱限制: 如果存储的数字不在提前设定的对应关系中, 能够正常存储, 也能够正常获取, 获取的结果为表中实际结果
3. 对应关系中的第一个参数不一定是数字, 也可以是字符串
'''
ajax简介
XML
xml也是一门标记语言, 应用场景:
- 写配置文件
- 写前端页面
- 应用于开发企业内部管理软件的框架odoo中
- odoo框架内部功能实现全部依赖于python2, 其中薪资功能的实现最复杂
AJAX
最大的优点是在不重新加载整个页面的情况下, 可以与服务器交换数据并更新部分网页内容
同步交互: 客户端发出一个请求后, 需要等待服务器响应结束后, 才能发出第二个请求
异步交互: 客户端发出一个请求后, 无需等待服务器返回响应, 就能发出第二个请求
'''
# ajax POST请求发送普通键值对数据, 实现页面数据局部刷新
1. 页面上有三个input框
2. 前两个框输入数字, 并将输入的数字用post请求提交到后端, 最后一个框展示两数之和
3. 要求页面不刷新
def ajax(request):
# print(request.is_ajax()) # 判断当前请求是否是ajax请求
# print(request.POST) # ajax发送的post请求, 普通的键值对也在request.POST中
if request.is_ajax():
d1 = request.POST.get('d1')
d2 = request.POST.get('d2')
res = int(d1) + int(d2)
return HttpResponse(res) # 结果返回给前端的异步回调函数, 一旦使用ajax请求, 数据只与回调函数交互
return render(request, 'ajax.html')
<script>
$('#d4').on(
'click', function () {
// 开启ajax语法的句式:
$.ajax({
url: '', // 指定前端提交ajax POST请求到后端时使用的url, 和form表单中的action用法一样
type: 'post', // 控制请求提交的方式, 不写默认提交get请求
data: {'d1': $('#d1').val(), 'd2': $('#d2').val()}, // 指定前端提交到后端的数据
success: function (data) { // data为异步提交数据后, 后端返回给前端的对该数据的处理结果
$('#d3').val(data) // 前端的回调函数处理后端的返回结果
}
})
}
)
</script>
'''
前后端数据交互的编码格式
前端向后端发送请求的途径
a标签的href参数: 只能发送GET请求
form表单: 可以发送GET| POST请求, 但发送的POST请求数据不能是json格式
ajax: 可以发送GET| POST请求, 发送的post请求数据默认编码格式为application/x-www-form-urlencoded
GET请求数据拼接在url后面: url?x=a&y=b
post请求数据的三种编码格式
- application/x-www-form-urlencoded, multipart/form-data, application/json
- 前端查看POST请求数据编码方式: f12-->Network-->Headers-->Request Headers-->Content-Type
- 前端查看POST请求数据: f12-->Network-->Headers-->Form Data-->view source
- django针对...-form-urlencoded编码格式的POST请求数据, 会自动解析并封装到request.POST中
- django针对multipart/formdata编码格式的POST请求数据, 会将满足...-form-urlencoded编码格式的数据解析到request.POST中, 将文件数据解析到request.FILES中
- 发送json格式数据只能借助ajax
- 在涉及前后端交互时, 要做到数据的格式与数据的编码格式一致
ajax POST请求发送json格式的数据
'''
<script>
$('#d4').on(
'click', function () {
$.ajax({
url: '',
type: 'post',
contentType: 'application/json', // 指定ajax POST请求数据的编码格式
data: JSON.stringify({'username': 'jason', 'password': '123'}), // 前端数据使用JSON.stringify序列化为json格式
success: function (data) {
alert(data.username)
}
})
}
)
</script>
def ajax(request):
from django.http import JsonResponse
import json
if request.is_ajax():
json_bin = request.body # 获取json格式二进制数据
json_str = json_bin.decode('utf8') # 解码
user_dic = json.loads(json_str) # 反序列化
return JsonResponse(user_dic) # JsonResponse会自动将后端的字典转换成前端的object类型数据
return render(request, 'ajax.html')
'''
ajax POST请求发送文件格式的数据
ajax传文件需要借助JavaScript中的内置对象
JavaScript实例化对象的关键字: new
FormData类的对象既可以携带文件数据, 也支持携带普通的键值对
'''
<script>
$('#d1').on(
'click',
function () {
var myFormData = new FormData(); // 实例化得到myFormData对象
myFormData.append('username', 'jason'); // 添加普通键值数据
myFormData.append('girl_img', $('#d2')[0].files[0]); // 获取input框中文件对象, 并添加到myFormData对象中
$.ajax({
...,
data: myFormData,
contentType: false, // 不使用myFormData对象内部自带的编码, 类似于json.dumps自动转码中文
processData: false, // 设置浏览器不处理发送的数据
success: function (data) {
console.log($('#d2')[0].files); // FileList {0: File, length: 1}
console.log($('#d2')[0].files[0]); // File {name: "666.jpg", ..., size: 35122, type: "image/jpeg" }
alert(data.username);
}
})
}
)
</script>
def ajax(request):
if request.is_ajax():
print(request.POST) # <QueryDict: {'username': ['jason']}>
print(request.FILES) # <MultiValueDict: {'girl_img': [<...: 666.jpg (image/jpeg)>]}>
...
'''
django内置的序列化模块
序列化的目的: 将数据整合成一个大字典的形式, 使前后端分离之后能方便地进行数据交互
def my_serialize(request):
from django.core import serializers
user_queryset = models.User.objects.all()
res = serializers.serialize('json', user_queryset) # json为要序列化成的数据格式
print(res, type(res)) # [{"model": "app01.user", "pk": 1, "fields": {"name": "nick", "gender": 1}}, ...] <class 'str'>
return HttpResponse(res)