关于Django中QuerySet对象的盘点笔记

QuerySet对象

传送门 ⇒ Model数据库请求

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字段
  1. related_name,默认为`model`_set(子表集)小写,由父模型获取子模型对象,注意用在模型对象上

  2. 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方法
  1. filter: 将满足条件的数据提取出来,返回一个新的QuerySet对象

    Book.objects.filter(price__gte=100)
    获取Book模型中定价price大于或等于100的数据

  2. exclude: 排除满足条件的数据,返回一个新的QuerySet对象

    Book.objects.exclude(name__contains='hello')
    获取Book模型中名称name不包含hello的数据

  3. annotate: 给QuerySet中的每一个模型对象添加(使用查询表达式:聚合函数、F表达式、Q表达式、Func表达式获取的结果)新字段属性

    Book.objects.annotate(AuthorName=F('author__name'))
    关联表查询一次性返回新的QuerySet对象,该对象有包含查询结果的新字段AuthorName
    book.authorname获取图书作者信息

  4. order_by: 查询时根据某字段以指定方式排序,默认由小到大,加上“-”表示反向排序,即由大到小,可以指定多个字段

    order_by可以结合annotate对数据进行跨表排序:
    Book.objects.annotate(saleNum=Count('bookorder')).order_by('-saleNum')
    根据QuerySet的懒查询进行联表排序:对Book数据按BookOrder订单统计结果大小排序

  5. 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中没有传入任何参数,则直接返回使用字段

  6. 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'))

  7. all: 获取ORM模型的QuerySet对象

  8. select_related: 同一次性获取当前对象及其引用的外键关联表(父表)的相关数据(多对一),不用重复访问数据库

    Book.objects.select_related('author', 'publisher')
    参数为外键字段(父表)的名称,对结果进行遍历,可以直接获取author,publisher信息

  9. 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)
    相当于FQ表达式的形式
    orders = books.bookorder_set.all()
    获取BookOrder表数据,返回QuerySet对象,但是不再额外查询数据库

  10. defer: 过滤掉某字段,返回包含其它字段的ORM模型对象集QuerySet

    books = Book.objects.defer('name')
    book模型除了name字段的所有其它字段数据
    book.name此时调用name属性时会重新发起一次查询请求

  11. only: 只选择某些字段,返回包含选择字段的ORM模型对象集QuerySet

    books = Book.objects.only('name')
    默认主键id必须获取(默认已包含)
    book对其它字段调用时会重新发起一次查询请求

  12. get: 获取满足条件的一个数据,返回一个ORM模型对象

    Book.objects.get(pk=1)
    给定的条件必须保证数据唯一且有效(一般使用主键),否则会报错

  13. creat: 在数据库插入一条数据,返回该数据的ORM模型对象,等效模型对象的save保存数据

    Book.objects.create(**kw)
    创建并保存一条新的数据,kw为各字段内容

  14. get_or_create: get一条数据,失败则create该数据,返回一个包含模型对象和是否被创建False/True元组

    Book.objects.get_or_create(name='NAME')
    该模型其它字段必须有默认值

  15. bulk_create: 一次性创建多条数据

    Book.objects.bulk_create([Book(name='n1'), Book(name='n2')])
    传入多个Book模型,一次性创建

  16. count: 获取模型对象的数量 SQL ==> count()函数 ,比len函数更加高效

    Book.objects.count()

  17. first/last: 返回QuerySet中的第一个/最后一个数据(Model对象)

  18. aggregate: 使用聚合函数 传送门 ⇒ aggregate & annotate

  19. exists: 判断某条件的数据是否存在,返回True/False

  20. 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识别去重

  21. update: 一次性更新所有对象的数据,可以结合F表达式使用,减少数据库请求次数

    Book.objects.update(price = F('price') + 5)
    所有图书价格提升5
    F表达式可以在SQL层面实现一次性更新,而不必要对每条数据分别请求更新

  22. delete: 一次性删除所有对象的数据

    Book.objects.filter(price__gte=90).delete()
    注意:删除数据时关联外键on_delete的设置

  23. 切片:由于切片是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语句并执行:
  1. 迭代QuerySet,即需要获取具体的ORM对象
  2. 切片时使用非默认步长,即已经无法再配合其它方法来整合SQL语句,注意切片后再使用filter方法会报错
  3. 对QuerySet调用len方法获取长度,也需要获取具体的ORM对象
  4. 对QuerySet调用list方法,也需要获取具体的ORM对象
  5. 对QuerySet进行bool转型,即也需要获取具体的ORM对象或None对象,这常用在判断语句
  6. 总的来说,没有需要具体的模型数据时,Django都会“懒查询”,等到需要时一起结合查询一次

related_name: 可以近似看作,这个表主动跟其他表关联了,那这个表的每条数据对外的称呼是related_name,其外键绑定数据经此回查
related_query_name: 可以看作,主动跟其它表关联了,在构建查询语句时通过这个来查询其关联数据
所以,关联表提供了3个沟通渠道:1,ForeignKey字段;2,related_name;3,related_query_name

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值