QuerySet
对象
文章目录
from django.db.models import Model, Prefetch
from django.db.models.manager import Manager
from django.db.models.query import QuerySet
type(Model.objects) == Manager # True
一、QuerySet
前情概要
多对一与一对多
多对一:表1的字段多次引用了表2的数据,可引用多次
一对多:表1的数据被表2的字段多次引用,可重复引用
可以认为,在树结构中(一个表为一层),一
表示上层数据的表(集合),多
表示下层数据的表(集合),一
是多
的父节点,多
是一
的子节点
django外键设置ForeignKey
字段
-
related_name
,默认为`model`_set
(子表集)小写,由父模型获取子模型对象,注意用在模型对象上 -
related_query_name
,默认为related_name/default_related_name
/`FieldName`
(字段名)小写,- 在构造
QuerySet
查询语句时,related_query_name
用于在父对象上调用子表数据; - 在子对象上需要调用父表的数据时,可以直接使用外键
ForeignKey
字段(默认绑定的是父表的主键)
ORM
对象的objects
方法属于django.db.models.manager.Manager
类型,
而Manager
是一个空壳类,其父类为动态生成的BaseManagerFromQuerySet
类,
其父类方法都是复制于django.db.models.query.QuerySet
的方法,
因此,objects
是一个具有QuerySet
类所有方法的Manager
类型。 - 在构造
二、QuerySet
方法
-
filter
: 将满足条件的数据提取出来,返回一个新的QuerySet
对象Book.objects.filter(price__gte=100)
⇒ 获取Book
模型中定价price
大于或等于100的数据 -
exclude
: 排除满足条件的数据,返回一个新的QuerySet
对象Book.objects.exclude(name__contains='hello')
⇒ 获取Book
模型中名称name
不包含hello的数据 -
annotate
: 给QuerySet
中的每一个模型对象添加(使用查询表达式:聚合函数、F
表达式、Q
表达式、Func
表达式获取的结果)新字段属性Book.objects.annotate(AuthorName=F('author__name'))
⇒ 关联表查询一次性返回新的QuerySet
对象,该对象有包含查询结果的新字段AuthorName
⇒book.authorname
获取图书作者信息 -
order_by
: 查询时根据某字段以指定方式排序,默认由小到大,加上“-”表示反向排序,即由大到小,可以指定多个字段order_by
可以结合annotate
对数据进行跨表排序:
Book.objects.annotate(saleNum=Count('bookorder')).order_by('-saleNum')
⇒ 根据QuerySet
的懒查询进行联表排序:对Book
数据按BookOrder
订单统计结果大小排序 -
values
: 只查询某些字段,同样返回一个QuerySet
对象,但是QuerySet
内不再是封装ORM
模型对象,而是一个字典Book.objects.values('id', 'name')
Book.objects.values('id', 'name', author_name=F('bookorder'))
Book.objects.values('id', 'name', saleNum=Count('bookorder'))
⇒ 如果values
中没有传入任何参数,则直接返回使用字段 -
values_list
: 同values
,但是返回的是元组的QuerySet
封装Book.objects.values_list('id', flat=True)
当且仅当只查询一个字段时,flat
参数可以设置为True
,输出扁平化数据
Book.objects.values_list('id', 'name', author_name=F('bookorder'))
Book.objects.values_list('id', 'name', saleNum=Count('bookorder'))
-
all
: 获取ORM
模型的QuerySet
对象 -
select_related
: 同一次性获取当前对象及其引用的外键关联表(父表)的相关数据(多对一),不用重复访问数据库Book.objects.select_related('author', 'publisher')
⇒ 参数为外键字段(父表)的名称,对结果进行遍历,可以直接获取author
,publisher
信息 -
prefetch_related
: 分两次获取当前对象及引用其外键的关联表(子表)的相关数据(一对多,多对多),不用大量访问数据库Book.objects.prefetch_related('bookorder_set')
⇒ 先查询所有Book
信息,再根据book.id
关联查找所有BookOrder
信息
Book.objects.prefetch_related('author')
⇒ (多对一)先查询Book
信息,再根据book.author
关联查找所有Author
信息,类似select_related
附加强调:找子表使用默认子表名_set
方式,找父表直接使用外键名(一般父表名)注意:查询出来的
books.bookorder_set
建议不进行filter
操作,因为主表对象变了,会产生更多查询,相当于prefetch_related
无效了Prefetch
函数,django.db.models.Prefetch
, 可以解决上述问题
prefetch = Prefetch('bookorder_set', queryset = BookOrder.objects.filter(price__gte = 90))
⇒ 先注册查询后操作
books.objects.prefetch_related(prefetch)
⇒ 相当于F
、Q
表达式的形式
orders = books.bookorder_set.all()
⇒ 获取BookOrder
表数据,返回QuerySet
对象,但是不再额外查询数据库 -
defer
: 过滤掉某字段,返回包含其它字段的ORM
模型对象集QuerySet
books = Book.objects.defer('name')
⇒book
模型除了name
字段的所有其它字段数据
⇒book.name
此时调用name
属性时会重新发起一次查询请求 -
only
: 只选择某些字段,返回包含选择字段的ORM
模型对象集QuerySet
books = Book.objects.only('name')
⇒ 默认主键id
必须获取(默认已包含)
⇒book
对其它字段调用时会重新发起一次查询请求 -
get
: 获取满足条件的一个数据,返回一个ORM
模型对象Book.objects.get(pk=1)
⇒ 给定的条件必须保证数据唯一且有效(一般使用主键),否则会报错 -
creat
: 在数据库插入一条数据,返回该数据的ORM
模型对象,等效模型对象的save
保存数据Book.objects.create(**kw)
⇒ 创建并保存一条新的数据,kw
为各字段内容 -
get_or_create
:get
一条数据,失败则create
该数据,返回一个包含模型对象和是否被创建False/True
元组Book.objects.get_or_create(name='NAME')
⇒ 该模型其它字段必须有默认值 -
bulk_create
: 一次性创建多条数据Book.objects.bulk_create([Book(name='n1'), Book(name='n2')])
⇒ 传入多个Book
模型,一次性创建 -
count
: 获取模型对象的数量 SQL ==>count()
函数 ,比len
函数更加高效Book.objects.count()
-
first/last
: 返回QuerySet
中的第一个/最后一个数据(Model对象) -
aggregate
: 使用聚合函数 传送门 ⇒ aggregate & annotate -
exists
: 判断某条件的数据是否存在,返回True/False
-
distinct
: 去除重复的数据Book.objects.filter(bookorder__price__gte=80).distinct()
⇒ 返回实际售价大于等于80的书籍数据,同一本书多次大于80出售时,查询结果可以去重,而不必要数据重复
BookOrder.objects.order_by('create_time').values('book_id').distinct()
⇒ 返回订单表中按出售时间排序的所有图书id,同一本书多次售出时,查询结果可以去重,而不必要数据重复
Book.objects.annotate(...).distinct()
⇒ 对标注后的数据进行去除注意:在有
order_by/annotate
时,distinct
可能会失效
因为:distinct
函数在SQL
层面为SELECT DISTINCT `book.id`, `book.name`,`...` from book ...
,
只有在DISTINCT
后所有字段都一样时才判断为重复数据,而annotate/order_by
会加入新查询字段,可能使原本重复数据变得不重复,而无法被DISTINCT
识别去重 -
update
: 一次性更新所有对象的数据,可以结合F表达式使用,减少数据库请求次数Book.objects.update(price = F('price') + 5)
⇒ 所有图书价格提升5
⇒F
表达式可以在SQL
层面实现一次性更新,而不必要对每条数据分别请求更新 -
delete
: 一次性删除所有对象的数据Book.objects.filter(price__gte=90).delete()
⇒ 注意:删除数据时关联外键on_delete
的设置 -
切片:由于切片是
QuerySet
类的特殊方法,Manager
对象没有复制,所以需要先获取QuerySet
对象再进行切片操作Book.objects.get_queryset()[1:3]
⇒SQL
实现:limit 2 offset 1
,获取从1号开始的两个数据
Book.objects.all()[0:3]
⇒ 获取前3个数据
三、结尾重点
Django生成QuerySet对象并不会马上实现SQL语句,再以下情况时才会转换成SQL语句并执行:
- 迭代QuerySet,即需要获取具体的ORM对象
- 切片时使用非默认步长,即已经无法再配合其它方法来整合SQL语句,注意切片后再使用filter方法会报错
- 对QuerySet调用len方法获取长度,也需要获取具体的ORM对象
- 对QuerySet调用list方法,也需要获取具体的ORM对象
- 对QuerySet进行bool转型,即也需要获取具体的ORM对象或None对象,这常用在判断语句
- 总的来说,没有需要具体的模型数据时,Django都会“懒查询”,等到需要时一起结合查询一次
related_name: 可以近似看作,这个表主动跟其他表关联了,那这个表的每条数据对外的称呼是related_name,其外键绑定数据经此回查
related_query_name: 可以看作,主动跟其它表关联了,在构建查询语句时通过这个来查询其关联数据
所以,关联表提供了3个沟通渠道:1,ForeignKey字段;2,related_name;3,related_query_name