Django:模型_QuerySet对象

QuerySet API

我们通常做查询操作的时候都是通过"模型名字.objects"的方式进行操作的。其实"模型名字.objects"是一个"django.db.models.manager.Manager"对象,而Manager这个类是一个"空壳"的类,它本身是没有任何的属性和方法的。它的方法全部都是通过Python以动态添加的方式,从QuerySet类中拷贝过来的(至于是如何拷贝的后面简单的介绍下,知道就好)

Django:模型_QuerySet对象

 

:本章的所有例子,大都基于这个模型类
Django:模型_QuerySet对象

 

例1:
⑴打印"模型名字.objects"
Django:模型_QuerySet对象

⑵查看"Manager"类
    ①通过步骤1的输出可以看到"模型名字.objects"返回的是一个"Manager"对象,因此查看"Manager"类
    ②导入"Manager"类:from django.db.models.manager import Manager
    ③选中"Manager"后Ctrl+B:查看源码
    
Django:模型_QuerySet对象

⑶查看"from_queryset"方法
Django:模型_QuerySet对象

注:
上面的源码什么的我是看得不是很懂的,只是想说明下:"objects"对象下面的方法都是从"QuerySet"对象下拷贝过来的,"QuerySet"对象对象下的方法,可以直接在"objects"对象下调用

 

 

返回新的QuerySet的方法

1、在使用QuerySet进行查找操作的时候,可以提供多种操作。比如过滤完后还要根据某个字段进行排序,那么这一系列的操作我们可以通过一个非常流畅的"链式调用"的方式进行。比如要从文章中获取标题为123,并且提取后要将结果根据发布时间进行排序,那么就可以通过下面的方式来完成:"article=Article.objects.filter(title='123').order_by('create_time')"

2、可以看到order_by方法是直接在filter()方法执行后调用的。这说明filter()方法返回的是一个拥有order_by方法的对象。而这个对象正是一个新的QuerySet对象,因此可以使用order_by方法(当然QuerySet对象还可以使用其他方法,这里只是以filter方法和order_by方法举例)

3、只要返回的数据是一个QuerySet对象,那么就可以调用QuerySet对象方法(链式调用或分步调用)。特别注意object对象中的方法是复制QuerySet对象下的方法的,因此object对象也可以直接调用QuerySet对象方法

4、本章介绍的都是QuerySet对象方法,因此这些方法都是可以组合使用的(除了有些QuerySet对象方法返回的不是QuerySet对象)

 

QuerySet对象方法

filter()

1、作用:将满足条件的数据提取出来,返回一个新的QuerySet对象

2、不论查询结果中有多少条数据都可以正常返回,无数据时也会返回一个空的QuerySet对象

例2:
⑴查看数据
Django:模型_QuerySet对象

⑵编辑视图:首次调用
Django:模型_QuerySet对象

注:
1、filter()方法是QuerySet对象下面的方法,那为什么objects对象也可以调用呢?Book.objects.filter(author_a_id__gte=1)
    ⑴这个就是我们前面介绍的:objects对象的方法等都是拷贝(继承)于QuerySet对象的,因此objects对象可以直接调用QuerySet对象下面的方法了

2、从上面的输出结果可以看到:QuerySet对象下的方法filter(这里以filter方法为例),返回的是一个新的QuerySet对象,因此在返回结果上又可以继续使用QuerySet对象下的方法了

⑶编辑视图:继续调用
Django:模型_QuerySet对象

注:
上面例子中在过滤"id不等于3"时,不能写成"books = books.filter(id!=3)":如果这样写的话会报错'bool' object is not iterable
    ⑴在Python中"!="和"=="等返回的是一个bool类型的值,但是这种类型的值不能使用在Django的模型中
    ⑵因此在Django在过滤"不等"条件时,需要换种处理方式:先使用Q表达式找出"等于这个值"的值,然后在进行取反操作:books.filter(~Q(id=3))

⑷编辑视图:优化
Django:模型_QuerySet对象

 

get()方法

1、作用:获取满足条件的数据

2、这个函数只能返回一条数据,并且如果给的条件能查询出多条数据的话,这个方法就会抛出异常;如果给的条件没有查询出任何数据的话,也会抛出异常。因此这个方法在获取数据的时候只能有且只有一条数据(通常使用于主键id来进行查询)

例3:
Django:模型_QuerySet对象

 

all()

作用:获取这个ORM模型的QuerySet对象。因为返回的是模型中的所有数据(本身不会有过滤作用),并且所有的这些数据是简单的放在一个QuerySet对象的

例4:
⑴编辑视图
Django:模型_QuerySet对象

⑵编辑视图:用在子查询中
Django:模型_QuerySet对象

⑶编辑视图
Django:模型_QuerySet对象

注:从上面例子可以看出
1、基于对象的查询(子查询)和基于双下划线的查询(join查询):在某些时候是可以达到同一目的的

 

values()

作用:用来提取指定的字段数据。默认情况下会把表中所有的字段全部提取出来,可以使用values()方法来指定只返回哪些字段,并且使用了values方法后,提取出的QuerySet对象中的数据模型不是模型,而是在valuses中指定的字段和值形成的字典

例5:
⑴编辑视图
Django:模型_QuerySet对象

注:从上面例子可以看出
1、有时候我们在表中查询数据的时候,并不是想把所有的字段都提取出来。我们有可能只是想要查询其中的几个字段,这个时候就可以使用values()方法了

2、values()方法返回值同样也是一个QuerySet对象,但是这个QuerySet对象中装的就不再是模型了,而是查询出来的数据组成的字典了

3、values()方法的作用只是返回指定字段的值,其并没有过滤作用,因此values()方法更多的是和filter()等方法一起使用的

例5_1:
⑴编辑视图:join正向查询
Django:模型_QuerySet对象

⑵编辑视图:join反向查询
Django:模型_QuerySet对象

⑶编辑视图
Django:模型_QuerySet对象

⑷编辑视图
Django:模型_QuerySet对象

⑸编辑视图:结合聚合函数
Django:模型_QuerySet对象

注:
1、在进行关联字段查询时,如果想更改一下返回结果中的键名时,可以使用关键字参数(加F表达式)来指定返回数据的键名

2、不过,自定义的名字不能和模型上本身拥有的字段名字一样,不然会报错

3、如果调用values()方法时,没有传递任何参数,那么会获取这个模型上的所有字段以及对应的值形成的字典

 

values_list()

作用:类似于values()方法,只不过返回的QuerySet对象中存储的不是字典而是元组

例6:
⑴编辑视图
Django:模型_QuerySet对象

⑵编辑视图
Django:模型_QuerySet对象

注:
1、values_list()方法如果只返回一个字段,那么可以给其添加一个flat=True的参数:返回结果就不在是一个元组了,而知这个字段的值

2、flat=True参数只能在只返回一个字段时使用,如果返回的字段数大于1,那么就会报错

3、values()方法和values_list()方法的作用只是返回指定字段的值,并没有过滤作用,因此两个方法更多的是和filter()等方法一起使用的(先通过其他方法过滤查询出数据,然后选择是否使用values()方法和values_list()方法来获取指定字段的值)

 

exclude()

作用:排除满足条件的数据,返回一个新的QuerySet对象

例7:
Django:模型_QuerySet对象

 

annotate()

作用:给QuerySet中的每个对象都添加一个使用查询表达式(可以是聚合函数、F表达式、Q表达式、Func表达式)的新字段

例8:查询每一本书的作者
⑴编辑视图:常规操作
Django:模型_QuerySet对象

⑵编辑视图
Django:模型_QuerySet对象

 

order_by()

作用:指定将查询的结果根据某个字段进行排序。如果要倒叙排序,那么可以在这个字段的前面加一个负号

例9:
⑴编辑视图
Django:模型_QuerySet对象

⑵增加模型类
Django:模型_QuerySet对象

⑶查看数据
Django:模型_QuerySet对象

⑷编辑视图
Django:模型_QuerySet对象

⑸模型类的ordering属性
Django:模型_QuerySet对象

 

select_related()

1、作用:在提取某个模型的数据的同时,也提前将相关联的数据提取出来

2、比如:提取文章数据时,可以使用select_related()方法将Author模型类中的数据提取出来,以后再使用article.author的时候就不需要再次去访问数据库了,可以减少数据库查询的次数

3、单独使用select_related()方法时是获取的全部数据,因此可以先使用filter()等方法过滤出数据后,再使用select_related()方法(这篇将的都是QuerySet对象的方法,因此只要是一个QuerySet对象,那么这篇文章中的所有方法基本上都可以混合使用)

4、select_related()只能从子表开始通过外键字段名来获取主表中的数据(7一对多关系中),但是不能通过主表开始获取子表中的数据
    ⑴这个方法只能用在外键的关联对象上,对于那种多对多的情况,不能使用这个方法来实现,而应该通过"prefetch_related"来实现

例10:
⑴编辑视图:常规方法
Django:模型_QuerySet对象

⑵编辑视图
Django:模型_QuerySet对象

⑶编辑视图
Django:模型_QuerySet对象

注:
1、使用常规方法时:每次在执行"book.author_a.name"时,即每次通过图书去查询作者时,都会去Author模型类中进行一次查询,这样的话就会耗性能,特别是有很多数据时

2、使用select_related()方法时,在查询Book模型类数据时,通过其方法中的参数就可以一次性的将关联表中的数据查询出来,后面就直接是获取Author中的数据了而不再需要再次查询

3、使用for循环来遍历QuerySet对象,然后依次返回每个数据的模型类,进而可以使用"模型类.字段名"来获取具体某个字段的值

4、只有返回数据为一个模型时,才能使用"模型类.字段名"来获取具体某个字段的值

5、注意:这个例子中是从子表到主表且表关系为一对多,因此主表中只会有一条数据,因此可以直接使用"模型类.字段名"来获取主表中具体某列的值
    ⑴如果是从主表到子表且表关系为一对多,因此子表中可能有多条或一条,如果子表数据为多条时,那么就需要先使用all()方法获取全部数据(此时为QuerySet对象),然后使用for循环遍历,然后才能使用"模型类.字段名"来获取子表中具体某列的值

 

prefetch_related()

1、作用:这个方法和select_related()方法类似,就是在访问多个表中的数据的时候,减少查询的次数

2、这个方法时为了解决多对一和多对多的关系的查询问题。例如要获取一个作者下的所有文章(一对多关系中:从主表到子表)

例11:
⑴编辑视图
Django:模型_QuerySet对象

⑵编辑视图
Django:模型_QuerySet对象

注:
使用"模型类.字段名"来获取某一个列具体的值时:
    ⑴前提是返回的是一个模型类,而不是一个QuerySet对象。如果是一个QuerySet对象,那么就需要通过for循环遍历来依次返回每个具体数据的模型类
    ⑵只有返回的数据是一条数据的模型类时,才能使用"模型类.字段名"来获取某一个列具体的值:如一对多中,子表到主表,主表肯定只有一条数据,那么就可以直接使用"模型类.字段名"来获取某一个列具体的值
    ⑶如果返回的是多条数据的模型类,那么就必须先使用for循环来遍历依次得到每条数据的模型类,然后才能使用"模型类.字段名"来获取某一个列具体的值:如一对多中,主表到子表,子表数据可以有一条或多条,当为多条时先使用all()来获取全部数据,然后遍历,再使用"模型类.字段名"
    ⑷其实不管使用什么方法,只要返回的是多条数据,那么都需要使用for循环来依次遍历出每条数据,然后才能使用"模型类.字段名"来获取某一个列具体的值。如果是对多条数据使用"模型类.字段名"的话肯定是不行的,都不知道获取的是哪条数据中的例值

 

defer()

1、作用:在一些表中,可能存在很多字段,但是一些字段的数据量可能是比较大的,而此时你又不需要。比如在获取文章列表的时候,文章的内容不是我们所需要的,因此这时候我们就可以使用defer()方法来过滤掉一些字段

2、defer()方法的作用与value()方法有点类似,只不过defer()方法返回的不是字典,而是模型

例12:
⑴编辑视图:可以传入多个字段
Django:模型_QuerySet对象

注:
1、从上面的SQL语句可以看到,查询文章的字段时,除了price,其他字段都查找出来了。当然,也可以使用book.price来获取这个文章的价格,但是会重新执行一个查询语句

2、defer()方法虽然能过滤字段,但是有些字段是不能过滤的,比如id列,即使你过滤了,也会提取出来

 

only()

作用:跟dfer()方法类似,只不过defer()方法是过滤指定的字段,而only是只提取指定的字段

例13:
⑴编辑视图:可以传入多个字段
Django:模型_QuerySet对象