聚合查询 / 分组查询 / F与Q查询 / orm中常见字段 / 自定义char字段 / 查询优化 / django如何开启事务
1. 聚合查询
# 聚合函数: aggregate
from django.db.models import Max,Min,Sum,Count,Avg
# 统计所有书的最高价格
res1 = models.Book.objects.all().aggregate(Max('price'))
print(res1) # {'price__max': Decimal('199.00')}
# 统计所有书的最低价格
res2 = models.Book.objects.all().aggregate(Min('price'))
print(res2) # {'price__min': Decimal('88.88')}
# 统计所有书总价格
res3 = models.Book.objects.all().aggregate(Sum('price'))
print(res3) # {'price__sum': Decimal('511.10')}
# 统计所有书的数量
res4 = models.Book.objects.all().aggregate(Count('price'))
print(res4) # {'price__count': 4}
# 统计所有书平均价格
res5 = models.Book.objects.all().aggregate(Avg('price'))
print(res5) # {'price__avg': 127.775}
res6 = models.Book.objects.all().aggregate(Max('price'),Min('price'),Sum('price'),Count('price'),Avg('price'))
print(res6)
2. 分组查询:annotate
# 1.统计每一本书的作者个数
res = models.Book.objects.annotate(author_num = Count('authors')).values('author_num')
print(res) # <QuerySet [{'author_num': 1}, {'author_num': 0}, {'author_num': 2}, {'author_num': 0}]>
# 2.统计出每个出版社卖的最便宜的书的价格
res = models.Publish.objects.annotate(price_min = Min('book__price')).values('price_min')
print(res) # <QuerySet [{'price_min': Decimal('88.88')}, {'price_min': Decimal('99.99')}, {'price_min': Decimal('199.00')}]>
# 3.统计不止一个作者的图书
res = models.Book.objects.annotate(author_obj = Count('authors')).filter(author_obj__gt=1).values('title')
print(res) # <QuerySet [{'title': '西游记'}]>
# 4.查询各个作者出的书的总价格
res = models.Author.objects.annotate(price_sum = Sum('book__price')).values('price_sum')
print(res) # <QuerySet [{'price_sum': Decimal('212.11')}, {'price_sum': Decimal('88.88')}, {'price_sum': None}]>
3. F与Q查询
"""
F与Q查询
我们之前在查询数据库的时候条件都是我们自己手写的
但是现在出现了条件是从数据库里面获取的
"""
from django.db.models import F,Q
# F查询
# 1.查询出卖出数大于库存数的书籍
res = models.Book.objects.filter(maichu__gt=F('kucun'))
print(res) # <QuerySet [<Book: 三国演义>, <Book: 西游记>, <Book: 红楼梦>]>
# 2.将所有的书的价格 全部提高100块
models.Book.objects.update(price = F('price')+100)
# 3.了解 尝试着将所有的书的名字后面都加上 爆款
from django.db.models.functions import Concat
from django.db.models import Value
models.Book.objects.update(title = Concat(F('title'),Value('爆款')))
# Q查询
# 1.查询书籍名称是西游记爆款或者价格是399的书
res = models.Book.objects.filter(Q(title='西游记爆款')|Q(price=399)) # 用来Q之后 就能够支持|表示或
print(res) # <QuerySet [<Book: 西游记爆款>, <Book: 红楼梦爆款>]>
res = models.Book.objects.filter(title='西游记爆款',price=399) # 逗号就是and
print(res) # <QuerySet []>
res = models.Book.objects.filter(~Q(title='西游记爆款') | Q(price=399)) # 波浪号 表示非
print(res) # <QuerySet [<Book: 三国演义爆款>, <Book: 水浒传爆款>, <Book: 红楼梦爆款>]>
# Q查询进阶用法 用Q产生对象 然后再使用
# 字符串的左边 跟你的变量名条件书写一模一样
q = Q()
q.connector = 'or' # 或
q.children.append(('title__icontains','p'))
q.children.append(('kucun',100))
res = models.Book.objects.filter(q)
print(res) # <QuerySet [<Book: p三国演义爆款>, <Book: P水浒传爆款>, <Book: 西游记爆款>]>
4. orm中常见字段
AutoField(primary_key=True)
# int自增列,必须填入参数 primary_key=True。当model中如果没有自增列,则自动会创建一个列名为id的列
CharField(max_length=32) # varchar(32)
# 字符类型,必须提供max_length参数, max_length表示字符长度
IntegerField() # int
# 一个整数类型,范围在 -2147483648 to 2147483647 (一般不用它来存手机号(位数也不够),直接用字符串存)
BigIntegerField() # long
# 长整型
DateField()
# 日期字段,日期格式 YYYY-MM-DD
# auto_now:每次修改数据的时候 都会更新该字段
# auto_now_add:只在创建数据的时候 才会自动将创建时间添加 后续不会再自动修改了
DecimalField()
# 浮点型
BooleanField(Field)
# 布尔值类型
# 该字段在存储数据的时候 你只需要传布尔值即可
# 对应到数据库中 会变成0/1
TextField(Field)
# 文本类型 存大段文本
EmailField(CharField) # varchar(...)
# 邮件类型
FileField(Field)
# 字符串,路径保存在数据库,文件上传到指定目录
# 参数:
# upload_to = "" 上传文件的保存路径
# storage = None 存储组件,默认django.core.files.storage.FileSystemStorage
DecimalField(Field)
# 10进制小数
# 参数:
# max_digits,小数总长度
# decimal_places,小数位长度
5. 自定义char字段
class MyCharField(models.Field):
def __init__(self,max_length,*args,**kwargs):
self.max_length = max_length
super().__init__(max_length=max_length,*args,**kwargs)
def db_type(self, connection):
return 'char(%s)'%self.max_length
'''
注:如果你使用的是django2.X版本 你在建数据库表关系的时候
你需要手动指定两个参数:
on_delete db_constraint
(级联更新 级联删除 是否建外键约束)
'''
6. 查询优化
# 惰性查询
# only()
res = models.Book.objects.all()
print(res) # <QuerySet [<Book: p三国演义爆款>, <Book: P水浒传爆款>, <Book: 西游记爆款>, <Book: 红楼梦爆款>]>
res = models.Book.objects.values('title')
print(res) # <QuerySet [{'title': 'p三国演义爆款'}, {'title': 'P水浒传爆款'}, {'title': '西游记爆款'}, {'title': '红楼梦爆款'}]>
res = models.Book.objects.only('title')
# print(res) # <QuerySet [<Book: p三国演义爆款>, <Book: P水浒传爆款>, <Book: 西游记爆款>, <Book: 红楼梦爆款>]>
for r in res:
print(r.title)
print(r.price)
'''
only会将括号内的字段对应的值 直接封装到返回给你的对象中 点该字段 不需要再走数据库
一旦你点了不是括号内的字段 就会频繁的去走数据库查询
'''
# defer() defer和only互为反关系
res = models.Book.objects.defer('title')
for r in res:
print(r.title)
print(r.price)
"""
defer会将括号内的字段排除之外将其他字段对应的值 直接封装到返回给你的对象中 点该其他字段 不需要再走数据库
一旦你点了括号内的字段 就会频繁的去走数据库查询
"""
# select_related: 表之间进行join连表操作,一次性获取关联的数据
res = models.Book.objects.select_related('publish')
print(res) # <QuerySet [<Book: p三国演义爆款>, <Book: P水浒传爆款>, <Book: 西游记爆款>, <Book: 红楼梦爆款>]>
for r in res:
print(r.publish)
print(r.publish_date)
res1 = models.Author.objects.select_related('author_detail')
print(res1) # <QuerySet [<Author: aa>, <Author: zz>, <Author: qq>]>
for r1 in res1:
print(r1.author_detail)
print(r1.author_detail.phone)
print(r1.author_detail.addr)
"""
select_related 会自动帮你做连表操作 然后将连表之后的数据全部查询出来封装给对象
select_related括号内只能放外键字段
并且多对多字段不能放
如果括号内外键字段所关联的表中还有外键字段 还可以继续连表
select_related(外键字段__外键字段__外键字段...)
"""
# prefetch_related: 多表连表操作时速度会慢,使用其执行多次SQL查询在Python代码中实现连表操作
res = models.Book.objects.prefetch_related('publish')
print(res) # <QuerySet [<Book: p三国演义爆款>, <Book: P水浒传爆款>, <Book: 西游记爆款>, <Book: 红楼梦爆款>]>
for r in res:
print(r.publish.name)
"""
prefetch_related 看似连表操作 其实是类似于子查询
prefetch_related括号内只能放外键字段
并且多对多字段不能放
如果括号内外键字段所关联的表中还有外键字段 还可以继续连表
select_related(外键字段__外键字段__外键字段...)
"""
"""
select_related 内部自动连表 消耗的资源就在连表上 但是走数据库的次数较少
prefetch_related 内部不做连表 消耗的资源就在查询次数上 但是给用户的感觉跟连表操作一样
"""
7. django如何开启事务
# 事务的四大特性:ACID
# 原子性 一致性 隔离性 持久性
# commit rollback
# 要么同时成功要么同时失败
# django中如何开启事务
from django.db import transaction
with transaction.atomic():
# 在该代码块中所写的orm语句 同属于一个事务
pass
# 缩进出来之后自动结束