django—ORM相关
常用的QuerySet方法
1、all()
查询表中所有数据,返回一个QuerySet对象列表
2、filter()
查询满足条件的数据,返回一个QuerySet对象列表
3、get()
查询指定的数据(存在且唯一的数据)
查询不到则报错,查询到多条数据也报错
4、order_by("字段")
将查询到的对象列表按照字段排序,默认是升序
"-字段"为降序
多字段排序order_by("字段1", "字段2")
优先按照字段1排序,字段1相等的数据再按照字段排序
5、reverse()
对已经排好序的对象列表进行翻转(用在order_by后面)
6、values()
将对象列表转换为字典列表
即[对象1,对象2...]《=============》[{对象1对应的数据字典},{对象2对应的数据字典}...]
传入指定参数,可以指定字典中只存放指定的字段和值
7、values_list()
将对象列表转换为元组列表
即[对象1,对象2...]《=============》[(对象1对应的所有数据的值),(对象2对应的所有数据的值)...]
传入指定参数,可以指定元组中只存放指定字段的值
8、distinct()
去重,查询结果有重复值时去重
如果是:
id=1,name=a,age=18
id=2,name=a,age=18
则不会去重,因为这两条数据的id不同
distinct不能传入指定参数去重
只能够通过查询方法,查询指定字段的数据后将重复值去重
9、count()
计数,统计数据的个数
效果和len()一样
效率高于len(),推荐QuerySet使用count计数
10、first()
获取对象列表中的第一个对象
11、last()
获取对象列表中的最后一个对象
12、exists()
判断对象列表是否为空,布尔值
13、exclude()
获取不满足条件的对象列表
与filter作用相反
单表的双下划线操作
在对QuerySet做条件查询的时候,使用双下滑操作可以实现一些逻辑的操作
比如:
需要查询id值大于某个值的所有数据时,django是不允许使用 filter(id>10)这种形式的,这就需要用到双下划线操作
1、小于某一个值(less than)
字段名__lt = 10 <=====> 字段值<10
2、大于(greater than)
字段名__gt = 10 <=====> 字段值>10
3、小于等于(less than equal)
字段名__lte = 10 <=====> 字段值<=10
4、大于等于(greater than equal)
字段名__gte = 10 <=====> 字段值>=10
5、大于等于 且 小于等于
字段名__range = [1,10] <=====> 1<=字段值<=10
range表示取一定区间内的值,左右均能取到(和python语法中的range不同,这里的10是可以取到的)
6、成员判断
字段名__in = [1,2,3,4,5]
这里的列表和 5 中的列表不同,5中的列表只接受两个值,表示一个区间范围
这里的列表可以存放多个值,条件判断的结果是满足成员判断的结果(即查询出来的结果只能是出现在列表中的)
7、字符串包含匹配
a、区分大小写
字段名__contains = "test"
匹配结果的改字段中必须包含test,如test01、01test、01test01均可被匹配到
b、不区分大小写 ( i表示ignore )
字段名__icontains = "test"
匹配结果的改字段中可以包含test或者TEST,如Test01、01TeSt、01test01均可被匹配到
8、字符串匹配以指定字符开头或者结尾
a、区分大小写
字段名__startwith = "xxx"
字段名__endwith = "xxx"
b、不区分大小写
字段名__istartwith = "xxx"
字段名__iendwith = "xxx"
9、对于日期类型的字段匹配
字段__year = "2020" 获取2020年的所有数据
字段__month = "1" 获取1月份的所有数据(查询结果可能为空,即使有相关数据)
字段__day = "1" 获取1日的所有数据(查询结果可能为空,即使有相关数据)
还可以用字符串方式匹配:
字段__contains = "2020-1-1" 获取2020年1月1日的数据
字段__contains = "-1-" 获取1月份的数据
10、获取指定字段为空的数据
字段__isnull = True
注意:空字符串并不是null,null是初始化时为空
外键相关查询操作
这里,简单举个1对多的关系:班级(1)和学生(多)的关系
此时创建表时,外键字段(cls)自然就设置在学生表中
1、基于对象的查询
a、正向查询(通过外键字段进行的查询,即1:n关系中n的那一方的对象查询1的那一方的对象)
通过一个学生对象,要想获取对应的班级信息:
student_obj.cls,即可获取到对应的班级对象
b、反向查询(通过对方的类名小写的方式查询,即1:n关系中1的那一方的对象查询n的那一方的对象。此时己方表中并没有对方的相关字段)
通过一个班级对象,要想获取对应的学生信息:
cls_obj.students_set,即可获取一个关系管理对象(反向查询的一个媒介,set就是集合的意思)
cls_obj.students_set.all(),即可获取到当前班级对象对应的所有学生对象(返回一个对象列表)
补充:关系管理对象的名称可以修改(如果不想使用 类名小写_set的形式获取关系管理对象)
在定义外键字段的时候,可以加上参数related_name = "",指定一个名称方便后续获取关系管理对象
设置related_name以后,就无法通过 类名小写_set 形式获取关系管理对象了
2、基于字段的查询
a、正向查询
根据外键字段信息查询
如需要查询所有班级名称为1的学生:
models.Students.objects.filter(cls__name="1") 这实际上是根据一个跨表查询的数据,来完成当前表的数据筛选
另外一种方式:
先查询一个名称为1的班级对象 cls_obj = models.Cls.objects.filter(name="1")
再查询1班的所有学生 models.Students.objects.filter(cls = cls_obj)
b、反向查询
通过学生名查询对应班级信息(此时班级表中没有学生的相关字段)
没有在外键指定 related_name时:
models.Cls.objects.filter(students__name="test"),此时只能通过类名小写形式进行双下划线跨表操作
在外键指定了related_name时:
models.Cls.objects.filter(这里换成指定的名称__name="test") 指定了related_name就只能用它查询,否则报错
如果觉得related_name在查询时使用不方便:
还以在外键中设置related_query_name指定一个查询时专用的名称,该名称只能用于查询字段时使用
同时设置了related_name、related_query_name在字段查询时只能用后者,获取关系管理对象时只能用前者
多对多关系的查询操作
多对多关系需要创建第三张表
在django模型类的定义中,提供了一个便利操作,无需定义第三个类
只需要在N:N关系的其中一方的模型类中定义一个ManytoMany字段即可,to参数可以是一个类名,也可以是类名的字符串形式(反射机制可以通过字符串拿到对应的类),ManytoMany字段和外键不同,它没有级联操作(delete on cascade等,更新数据需要在逻辑业务中实现)
例如,书籍与作者的关系(n:n),假设ManytoMany字段定义在作者表中(book)
1、基于对象的查询
a、通过作者对象获取对应的的书籍信息(正向查询,因为ManytoMany字段在作者表中)
author_obj.book,此时获取到的依然是一个关系管理对象(与外键操作不同,外键操作中N的那一方直接通过外键字段获取到另一方的数据对象,是因为另一方是唯一确定的,而多对多关系中,双方获取对方信息的个数是不确定的)
b、通过书籍对象获取对应的作者信息(方向查询,查询方式和外键操作中一致)
book_obj.authors_set,作者类名_set的形式获取到关系管理对象,此时同样可以在ManytoMany字段中设置related_name修改关系管理对象的名称
2、基于字段的查询
和外键查询操作如出一辙
同样可以设置ManytoMany字段中的related_query_name来修改字段查询的名称
3、通过一个作者对象设置作者和书籍之间的对应关系
author_obj.book,获取关系管理对象
author_obj.book.set([ ])通过关系管理对象下的set方法,传入一个列表,列表中可以是多个书籍的的id或者书籍对象
完成设置,即可将关系表中的对应关系修改完成
关系管理对象的方法
1、all方法 获取该关系管理对象下的所有对象信息
2、set方法 设置多对多关系(或者1对多关系)
设置多对多关系时
set方法接收的列表,可以是多个id值,也可以是多个对象
设置1对多关系时,
set方法接收的列表,只能是对象列表
3、add方法 添加多对多关系(或者1对多关系)
往关系表中新增数据,add方法接收可变长的位置参数,即add(*args)形式
添加多对多关系时
可以传入的是id,或者是对象
添加1对多关系时
只能传入对象
4、remove 删除多对多关系(或者1对多关系)
同样接收一个可变长的位置参数
注:删除1对多关系时,需要将外键指定设置为null=True否则无法删除
5、clear 清空关系表
不接收任何参数
注:清空1对多关系时,同样需要将外键指定设置为null=True否则无法清空
6、create 新增一个对象,且和当前对象建立关系
author_obj.books.create(name="测试")
新增一本书《测试》存入书籍表,并且与当前作者建立关系,存入关系表中
聚合和分组
1、django常用的内置聚合函数:
form django.db.models import Max, Min, Count, Sum, Avg
Max:最大值
Min:最小值
Count:计数
Sum:求和
Avg:求平均值
对于QuerySet执行聚合操作需要用到聚合方法aggregate(),在聚合方法内使用相应的聚合函数即可
对象列表.aggregate(Max("指定字段")) 求某个字段的最大值
返回结果是一个字典:{"指定字段名__max": 对于的值},键名是由字段名 + 双下划线 + 相应聚合函数的小写组成
如果想要指定字典中键的名称,可以将方法内函数写成关键字形式,如:对象列表.aggregate(max=Max("指定字段")),这样就指定了键的名称为max
aggregate也称为终止子句,因为该方法返回的结果不再是QuerySet,因此后续无法再进行相关查询操作。
2、分组(group by)
分组使用到的方法:annotate
统计每本书的作者个数:
models.Book.objects.annotate(Count("authors"))
models.Book.objects:表示按书进行分组
Count("authors"):统计分组后每本书的作者个数,因为多对多字段定义在作者类中,所以Count指定的字段为作者表的小写(或者related_name如果指定了该属性的话)
annotate:负责将统计的结果,作为属性值插入到每本书的对象中,然后返回一个对象列表
annotate实现了分组的过程
实际上将分组聚合后的结果添加到了每个分组的对象上
F查询和Q查询
from django.db.models import F,Q
1、F查询
获取比较同一张表中不同字段的结果
F("字段"):获取当前对象中指定字段的值
如获取书籍表中,库存大于销量的书
models.Book.objects.filter(库存__gt=F("销量"))
对应的sql:select book_name from books where 库存>销量
2、Q查询,进行与(&)或(|)非(~)操作
如获取id值大于5或者小于3的数据
models.Book.objects.filter(Q(id__gt=5)| Q(id__lt=3))
事务
from django.db import transaction
执行事务:
with transaction.atomic():
# with内部执行一系列操作(要么都执行,要么都不执行)
执行事务过程中 ,中途报错的话会触发回滚机制,之前的操作全部撤销
为了不影响代码正常运行,需要进行异常捕获
注意:异常捕获必须在with外部 ,也就是将整个with子句包括起来,才能正常执行事务
如果在with内部进行异常捕获的,既然异常能够正确被处理,却无法触发回滚机制,异常发生前的所有操作不会被撤销。