加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > Python > 正文

21.QuerySetAPI

发布时间:2020-12-20 12:53:45 所属栏目:Python 来源:网络整理
导读:? QuerySet API: 我们通常做查询操作的时候,都是通过模型名字.objects的方式进行操作。其实模型名字.objects是一个django.db.models.manager.Manager对象,而Manager这个类是一个“空壳”的类,他本身是没有任何的属性和方法的。他的方法全部都是通过Pytho

?

QuerySet API:

我们通常做查询操作的时候,都是通过模型名字.objects的方式进行操作。其实模型名字.objects是一个django.db.models.manager.Manager对象,而Manager这个类是一个“空壳”的类,他本身是没有任何的属性和方法的。他的方法全部都是通过Python动态添加的方式,从QuerySet类中拷贝过来的。

模型.objects:

这个对象是django.db.models.manager.Manager的对象,这个类是一个空壳类,他上面的所有方法都是从QuerySet这个类上面拷贝过来的。因此我们只要学会了QuerySet,这个objects也就知道该如何使用了。
Manager源码解析:

class_name = "BaseManagerFromQuerySet"

class_dict = {
    ‘_queryset_class‘: QuerySet
}

class_dict.update(cls._get_queryset_methods(QuerySet))

# type动态的时候创建类
# 第一个参数是用来指定创建的类的名字。创建的类名是:BaseManagerFromQuerySet
# 第二个参数是用来指定这个类的父类。
# 第三个参数是用来指定这个类的一些属性和方法
return type(class_name,(cls,),class_dict)

_get_queryset_methods:这个方法就是将QuerySet中的一些方法拷贝出来

filter

将满足条件的数据提取出来,返回一个新的QuerySet。具体的filter可以提供什么条件查询

exclude

排除满足条件的数据,返回一个新的QuerySet。示例代码如下:

Article.objects.exclude(title__contains=‘hello‘)


以上代码的意思是提取那些标题不包含hello的图书。

annotate

给QuerySet中的每个对象都添加一个使用查询表达式(聚合函数、F表达式、Q表达式、Func表达式等)的新字段。示例代码如下:

articles = Article.objects.annotate(author_name=F("author__name"))


以上代码将在每个对象中都添加一个author__name的字段,用来显示这个文章的作者的年龄。

aggregate

使用聚合函数。

order_by:

# 根据创建的时间正序排序
articles = Article.objects.order_by("create_time")
# 根据创建的时间倒序排序
articles = Article.objects.order_by("-create_time")
# 根据作者的名字进行排序
articles = Article.objects.order_by("author__name")
# 首先根据创建的时间进行排序,如果时间相同,则根据作者的名字进行排序
articles = Article.objects.order_by("create_time",‘author__name‘)

一定要注意的一点是,多个order_by,会把前面排序的规则给打乱,而使用后面的排序方式。比如以下代码:

articles = Article.objects.order_by("create_time").order_by("author__name")

他会根据作者的名字进行排序,而不是使用文章的创建时间。
当然,也可以在模型定义的在Meta类中定义ordering来指定默认的排序方式。示例代码如下:

class Meta:
        db_table = ‘book_order‘
        ordering = [‘create_time‘,‘-price‘]

还可以根据annotate定义的字段进行排序。比如要实现图书的销量进行排序,那么示例代码如下:

books = Book.objects.annotate(order_nums=Count("bookorder")).order_by("-order_nums")
    for book in books:
        print(‘%s/%s‘%(book.name,book.order_nums))

values

values的返回值同样也是一个QuerySet对象,但是这个QuetySet中装的就不是模型了,而是一个一个的dict字典.

如果我们想要提取的是这个模型上关联的对象的属性,那么也是可以的,查找顺序跟filter的用法是一样的.示例代码如下:

books = Book.objects.values("id","name","author__name")


以上将会提取author模型下的name字段,如果我们不想要这个名字,想要更改一个名字,那么可以使用关键字参数.示例代码如下:

books = Book.objects.values("id",author_name=F("author__name"))


自定义的名字,不能和模型上本身拥有的字段一样,比如以上author_name如果取名叫做author就会报错,因为Book上本身就拥有一个字段叫做author.

values中,也可以使用聚合函数来形成一个新的字段.比如我想要获取每本图书的销量,那么示例代码如下:

books = Book.objects.values("id",order_nums=Count("bookorder"))

如果调用values方法的时候,没有传递任何的参数,那么会获取这个模型上的所有字段以及对应的值形成的字典.示例代码如下:

books = Book.objects.values()

values_list

d跟values是一样的作用.只不过这个方法返回的QuerySet中,装的不是字典,而是元组,示例代码如下:

books = Book.objects.values_list("id","name")


那么以上代码的返回结果是:

(1,"西游记")

如果给values_list只指定一个字段,那么我们可以指定flat=True,这样返回回来的结果就不在是一个元组,而是这个字段的值,示例代码如下:

books = Book.objects.values_list("name",flat=True)


那么以上返回的结果是:

`三国演义`


一定要注意的是,flat只能用在只有一个字段的情况下,否则就会报错.

all方法

查询模型下的所有数据,返回一个QuerySet对象,这个QuerySet对象没有经过任何的修改(比如过滤等)

在查找某个表的数据的时候,可以一次性把相关联的其他表的数据都提取出来,这样可以在以后访问相关联的表的数据的时候,不用再次查找数据库,可以节省一些开销.示例代码如下:

books = Book.objects.select_related("author","publisher")
for book in books:
    print(book.author.name)
    # 因为在提取book的时候,使用了select_related,那么以后在访问book.author的时候,不会再次向数据库重新发起查询


注意:这个方法只能用在外键关联的对象上,对于那种多对多,或者多对一的情况不能使用它在实现,而应该使用prefetch_related来实现.

这个方法类似与select_related方法,也是用来查询语句的时候,提前将查找的数据提取出来.不过这个方法是用来解决多对多,非外键的的情况.这个方法会产生两个查询语句.所以,如果查询外键关联的模型就用select_related,如果查询的是多对多或者非外键关联的情况,就用prefetch_related.示例代码如下:

books = Book.objects.prefetch_related("bookorder_set")


需要注意的是:在使用prefetch_related查找出来的boororder_set,建议不要再对它进行任何操作,比如filter,不然又会产生N多查询语句,影响查询的性能.比如以下的代码是不对的:

books = Book.objects.prefetch_related("bookorder_set")
for book in books:
    print(book.name)
    # 这个地方如果对bookorder_set进行了操作,那么就又会产生新的sql语句,前面的prefetch_related就相当于白做了
    oredrs = book.bookorder_set.fliter(price__gte=90)
    for order in orders:
        print(order.id)


那么如果确实是想要对预先查找的集合进行操作,那么我们可以使用django.db.models.Prefetch来完成,示例代码如下:

# 先使用Prefetch把查找的条件写好,在放到prefetch_related中
from django.db.models import Prefetch
prefetch = Prefetch("bookorder_set",queryset=Bookorder.objects.filter(price__gte=90))
books = Book.objects.prefetch_related(prefetch)
for book in books:
    print(book.name)
    orders = book.bookorder_set.all()
    for order in orders:
        print(order.id)

defer和only

这两个方法都会返回一个QuerySet,并且这个QuerySet中装的都是模型,而不是字段
1. defer: 这个方法用来告诉ORM,在查询某个模型的时候,过滤到某些字段.
2. only: 这个方法用来告诉ONR,只提取某些字段.
注意: 使用defer了的字段,以后在使用这个字段,会重新发起一次请求,因此要谨慎操作,only也同理.示例代码如下:

articles = list(Article.objects.defer("title"))
for article in articles:
    # 因为在上面提取的时候过滤了title
    # 这个地方重新获取title,将重新向数据库中进行一次查找操作
    print(article.title)
for sql in connection.queries:
    print(‘=‘*30)
    print(sql)

get

获取满足条件的数据,返回的是具体的模型。这个函数只能返回一条数据,并且如果给的条件有多条数据,那么这个方法会抛出MultipleObjectsReturned错误,如果给的条件没有任何数据,那么就会抛出DoesNotExit错误。所以这个方法在获取数据的只能,只能有且只有一条。

create

创建一条数据,并且保存到数据库中。这个方法相当于先用指定的模型创建一个对象,然后再调用这个对象的save方法。示例代码如下:

article = Article(title=‘abc‘)
article.save()

# 下面这行代码相当于以上两行代码
article = Article.objects.create(title=‘abc‘)

bulk_create

一次性创建多个数据,不管多少条数据,一条SQL语句解决

get_or_create

如果给定的条件有数据,那么就会把这个数据直接提取出来.如果给定的条件没有数据,那么就会先创建数据,然后再把数据返回回来.

count

count:获取提取的数据的个数。如果想要知道总共有多少条数据,那么建议使用count,而不是使用len(articles)这种。因为count在底层是使用select count(*)来实现的,这种方式比使用len函数更加的高效。

first和last

返回QuerySet中的第一条和最后一条数据,返回值是数据的模型.

exisit

exists:判断某个条件的数据是否存在。如果要判断某个条件的元素是否存在,那么建议使用exists,这比使用count或者直接判断QuerySet更有效得多。示例代码如下:

if Article.objects.filter(title__contains=‘hello‘).exists():
    print(True)
比使用count更高效:
if Article.objects.filter(title__contains=‘hello‘).count() > 0:
    print(True)
也比直接判断QuerySet更高效:
if Article.objects.filter(title__contains=‘hello‘):
    print(True)

distinct

distinct:去除掉那些重复的数据。这个方法如果底层数据库用的是MySQL,那么不能传递任何的参数。比如想要提取所有销售的价格超过80元的图书,并且删掉那些重复的,那么可以使用distinct来帮我们实现,示例代码如下:

books = Book.objects.filter(bookorder__price__gte=80).distinct()


需要注意的是,如果在distinct之前使用了order_by,那么因为order_by会提取order_by中指定的字段,因此再使用distinct就会根据多个字段来进行唯一化,所以就不会把那些重复的数据删掉。示例代码如下:

orders = BookOrder.objects.order_by("create_time").values("book_id").distinct()


那么以上代码因为使用了order_by,即使使用了distinct,也会把重复的book_id提取出来。

update

一次性可以把所有的数据都更新完
update:执行更新操作,在SQL底层走的也是update命令。比如要将所有category为空的article的article字段都更新为默认的分类。示例代码如下:

Article.objects.filter(category__isnull=True).update(category_id=3)


注意这个方法走的是更新的逻辑。所以更新完成后保存到数据库中不会执行save方法,因此不会更新auto_now设置的字段。

delete

delete:删除所有满足条件的数据。删除数据的时候,要注意on_delete指定的处理方式。

切片操作

切片操作:有时候我们查找数据,有可能只需要其中的一部分。那么这时候可以使用切片操作来帮我们完成。QuerySet使用切片操作就跟列表使用切片操作是一样的。示例代码如下:

books = Book.objects.all()[1:3]
for book in books:
    print(book)


切片操作并不是把所有数据从数据库中提取出来再做切片操作。而是在数据库层面使用LIMIE和OFFSET来帮我们完成。所以如果只需要取其中一部分的数据的时候,建议大家使用切片操作。

什么时候Django会将QuerySet转换为SQL去执行:

生成一个QuerySet对象并不会马上转换为SQL语句去执行。
比如我们获取Book表下所有的图书:

books = Book.objects.all()
print(connection.queries)


我们可以看到在打印connection.quries的时候打印的是一个空的列表。说明上面的QuerySet并没有真正的执行。
在以下情况下QuerySet会被转换为SQL语句执行:

  1. 迭代:在遍历QuerySet对象的时候,会首先先执行这个SQL语句,然后再把这个结果返回进行迭代。比如以下代码就会转换为SQL语句:
    for book in Book.objects.all():
         print(book)
  2. 使用步长切片操作:QuerySet可以类似于列表一样做切片操作。做切片操作本身不会执行SQL语句,但是如果如果在做切片操作的时候提供了步长,那么就会立马执行SQL语句。需要注意的是,做切片后不能再执行filter方法,否则会报错。

  3. 调用len函数:调用len函数用来获取QuerySet中总共有多少条数据也会执行SQL语句。

  4. 调用list函数:调用list函数用来将一个QuerySet对象转换为list对象也会立马执行SQL语句。

  5. 判断:如果对某个QuerySet进行判断,也会立马执行SQL语句。

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读