Django
1 聚合查询
关键字 aggregate
from django.db.models import Max, Min, Sum, Count, Avg
from app01 import models
res = models.Book.objects.aggregate(Max('price'), Min('price'), Sum('price'), Count('pk'), Avg('price'))
2 分组查询
关键字 annotate
默认按照主键字段分组
from django.db.models import Max, Min, Sum, Count, Avg
from app01 import models
# 1. 统计每一本书的作者个数
# res = models.Book.objects.annotate() # 按照书进行分组
res = models.Book.objects.annotate(author_num=Count('authors')).values('title', 'author_num')
# 书查作者,正向查询按字段。
# authors__id可以简写为authors,内部自动查询其主键。
# 2. 统计每个出版社卖的最便宜的书的价格
# res = models.Publish.objects.annotate() # 按照出版社进行分组
res = models.Publish.objects.annotate(min_price=Min('book__price')).values('name', 'min_price')
# 出版社查书,反向查询按表名小写
# 3. 统计不止一个作者的图书
# 先按照图书分组,求每一本书对应的作者个数;
# 过滤出作者数量大于1的图书。
res = models.Book.objects.annotate(author_num=Count('authors')).filter(author_num__gt=1).values('title', 'author_num')
# 只要orm语句获得的结果是一个queryset对象,那么它就可以继续调用queryset对象的方法。
# 4. 查询每个作者出的书的总价格
res = models.Author.objects.annotate(sum_price=Sum('book__price')).values('name', 'sum_price')
按照指定的字段分组
values
# 按照书的价格分组
models.Book.objects.values('price').annotate()
注意,如果如果出现分组查询报错的情况,需要修改数据库严格模式。
MySQL中,严格模式 ONLY_FULL_GROUP_BY
分组后默认只能获取分组的依据字段,其它字段无法直接获取。
3 F查询
F查询,用于直接获取/操作表中指定字段的数据。
- 查询出库数大于入库数的书籍
from django.db.models import F
res = models.Book.objects.filter(output__gt=F('input'))
- 将所有书籍的价格提升500块
from django.db.models import F
models.Book.objects.update(price=F('price') + 500)
- 将所有书的名称后面加上‘爆款’两个字
操作字符类型的数据时,F查询不能直接进行字符串拼接。
from django.db.models.functions import Concat
from django.db.models import Value
models.Book.objects.update(title=Concat(F('title'), Value('爆款')))
4 Q查询
4.1 基本使用
Q包裹的条件之间的连接关系支持与、或、非。
查询出库数大于100或者价格小于600的书籍
from django.db.models import Q
# and ,
res_and = models.Book.objects.filter(Q(output__gt=100), Q(price__lt=600))
# or |
res_or = models.Book.objects.filter(Q(output__gt=100) | Q(price__lt=600))
# not ~
res_not = models.Book.objects.filter(~Q(output__gt=100))
# 组合使用
res_not = models.Book.objects.filter(~Q(output__gt=100) | Q(price__lt=600))
4.2 Q的高阶用法
前面设置查询条件时,条件等号左边是变量名的形式,
Q支持查询条件的左边的值以及条件连接关系是字符串的形式,即可以动态指定查询条件。
from django.db.models import Q
q = Q() # 生成Q对象
q.connector = 'or' # and/or,连接关系不指定默认是and。
q.children.append(('output__gt', 100)) # 注意,参数接收可迭代对象,可以是元组或列表。
q.children.append(('price__lt', 600))
res = models.Book.objects.filter(q)
5 事务
事务的四个特性:ACID
A:原子性,不可分割的最小单位;
C:一致性,事务前后处于一致状态;
I:隔离性,事务与事务之间互不干扰;
D:持久性,事务确认后永久生效。
事务回滚:rollback
事务确认:commit
Django中如何开启事务?
from django.db import transaction
try:
with transaction.atomic():
# with代码块内的所有orm操作都属于同一个事务
...
except Exception as e:
print(e)
print('执行其它操作。')
6 字段
6.1 常用字段
- AutoField 主键字段
参数:
primary_key=True (必需参数) - CharField
对应的类型是varchar
参数:
max_length 字符长度 - IntegerField
对应的类型是int - BigIntegerField
对应的类型是bigint - DecimalField
参数:
max_digits:总位数
decimal_places:小数位 - EmailField
对应的是varchar(254) - DateField和DateTimeField
参数:
auto_now:每次修改数据都会更新该时间字段;
auto_now_add:创建数据时自动记录创建时间,之后一般不会修改。 - BooleanField
对应的是布尔类型。
字段传入布尔值(False/True),数据库中存储0/1 。 - TextField
用于存储大量文本内容,没有字数限制。 - FileField
用于上传文件,
为该字段传一个文件对象,将文件保存到参数upload_to指定的目录下,将文件的存储路径保存到数据库中。
参数:
upload_to = “” 上传文件的保存目录;
storage = None 存储组件,默认。
其它参考:https://www.cnblogs.com/Dominic-Ji/p/9203990.html
6.2 常用字段参数
verbose_name、
字段注释
null
用于表示某个字段可以为空。
unique
如果设置unique=True,该字段在此表中必须唯一。
一对一中,ForeignKey(unique=True) 等价于 OneToOneField(),即一对一关系有两种指定方式。
db_index
如果设置db_index=True,表示为该字段创建索引。
default
为该字段设置默认值。
6.3 外键字段ForeignKey
外键字段通常用于表示与其它表的关联关系,一般把ForeignKey字段设置在’一对多’中’多’的一方。
ForeignKey既可以用于与其它表做关联,也可以用于和自身做关联。
参数:
to
设置关联的表
to_field
设置关联的表的字段,默认关联主键字段。
on_delete
删除关联表中的数据时,当前表与其关联的数据的行为。
django 2.X 及以上版本,需要指定外键字段的级联更新和级联删除。
on_delete = models.CASCADE
删除关联数据,与之关联也删除
db_constraint
是否在数据库中创建外键约束,默认为True。
6.4 自定义字段
models.py
# 自定义字段
class MyCharField(models.Field):
def __init__(self, max_length, *args, **kwargs):
self.max_length = max_length
# 最后调用父类的init方法
# max_length一定要以关键字的形式传入
super().__init__(max_length=max_length, *args, **kwargs)
def db_type(self, connection):
# 返回真正的数据类型及约束条件
return 'char(%s)' % self.max_length
# 自定义字段使用
class Book(models.Model):
mycharfield = MyCharField(max_length=32, null=True)
7 数据库查询优化
7.1 only 与 defer
ORM的特点:惰性查询
如果仅仅是ORM语句,在后面没有用到该语句查询出来的结果,ORM会自动识别并不执行这条语句。
res = models.Book.objects.all()
# 这条ORM查询语句不会执行,因为后面没有使用res。
res = models.Book.objects.all()
print(res) # 这里使用了res,执行ORM查询语句。
获取书籍表中所有数的名字
res = models.Book.objects.values('title')
for each_book in res:
print(each_book.get('title'))
7.1.1 only
only方法返回QuerySet对象,类似于列表套对象,且对象中只有only的参数指定的属性,如果后面访问only未指定的属性,则会重新检索数据库。
需求:得到的数据是一个数据对象,然后点title就直接获取书名,并且没有其它字段。
res = models.Book.objects.only('title')
print(res) # <QuerySet [<Book: 三国演义>, <Book: 红楼梦>]>
for each_book in res:
print(each_book.title) # 对于only的括号内指定的字段,循环中不会再次查询数据库;
print(each_book.price) # 对于only的括号内没有指定的字段,每一次循环都会重新查询一次数据库。
res = models.Book.objects.all()
for each_book in res:
# all 循环中不会查询数据库
print(each_book.title)
print(each_book.price)
7.1.2 defer
defer与only相反
defer括号内指定的字段不在查询出来的对象内,每一次查询defer指定的字段都需要重新查询数据库,
如果查询的是defer括号内没有指定的字段,不需要重新查询数据库。
res = models.Book.objects.defer('title')
print(res) # <QuerySet [<Book: 三国演义>, <Book: 红楼梦>]>
for each_book in res:
print(each_book.title) # 对于defer的括号内指定的字段,每一次循环都会重新查询一次数据库;
print(each_book.price) # 对于defer的括号内没有指定的字段,循环中不会再次查询数据库。
7.1.3 总结
访问only括号内指定的字段不需要重新检索数据库,访问其它字段则需要重新检索数据库。
def与only正好相反。
only与defer的选择,取决于字段个数。
7.2 select_related 与 prefetch_related
select_related 与 prefetch_related与跨表操作有关。
7.2.1 select_related 联表操作
res = models.Book.objects.all()
for each_book in res:
print(each_book.publish.name) # 每循环一次就查询一次数据库
res = models.Book.objects.select_related('authors') # INNER JOIN
for each_book in res:
print(each_book.publish.name) # 不需要再次查询数据库
select_related在内部先将book表与publish表连起来进行查询,
然后将联表查询的结果全部封装成对象。
此时无论从对象中获取book表的数据还是publish表的数据,都不需要再次查询数据库。
注意:select_related括号内只能放外键字段,
因此只支持一对多和一对一,不支持多对多。
可以一次连接多张表,将多张表的检索结果封装到对象中。
models.表名.objects.select_related('外键字段1__外键字段2__外键字段3')
7.2.2 prefetch_related 子查询
res = models.Book.objects.prefetch_related('publish') # 子查询
for each_book in res:
print(each_book.publish.name)
prefetch_related方法在内部其实就是子查询,将子查询得到的所有结果全部封装到对象中,好像也是一次性搞定的,使用感觉上与select_related差别不大。
7.2.3 总结
select_related 联表操作 内部只需查询一次;
select_related的参数只接受外键字段,因此只支持一对多和一对一,不支持多对多。
prefetch_related 子查询 内部先查询子表,将子表的查询结果当作另一张表的条件再次进行查询,总共查询两次。
select_related与prefetch_related选择
取决于联表耗时长还是再次检索数据库的耗时长。