Django ORM注释annotate()、过滤filter()与返回值values()顺序的相互作用

https://docs.djangoproject.com/en/3.1/topics/db/aggregation/

Order of annotate() and filter() clauses

annotate()和filter()子句的顺序

When developing a complex query that involves both annotate() and filter() clauses, pay particular attention to the order in which the clauses are applied to the QuerySet.

当开发一个涉及annotate()和filter()子句的复杂查询,特别注意应用到QuerySet的子句的顺序。

When an annotate() clause is applied to a query, the annotation is computed over the state of the query up to the point where the annotation is requested. The practical implication of this is that filter() and annotate() are not commutative operations.

当一个注释子句被应用到一个查询则直到注释被请求时才基于查询的状态开始计算注释实际的含义是filter()annotate()不是可交换的操作

 Given:

 

Publisher A has two books with ratings 4 and 5.

Publisher B has two books with ratings 1 and 4.

Publisher C has one book with rating 1.

Here’s an example with the Count aggregate:

#~~~~~~~~~~

>>> a, b = Publisher.objects.annotate( num_books=Count('book', distinct=True) ).filter(book__rating__gt=3.0) #Publisher反向外键引用Book表,一对多,先是返回每个Publisher记录增加一个num_books注释列的查询集,在此基础上反向外键引用Book过滤至少有一本书评分高于3分的出版商

>>> a, a.num_books

(<Publisher: A>, 2)

>>> b, b.num_books

(<Publisher: B>, 2)

 

>>> a, b = Publisher.objects.filter( book__rating__gt=3.0 ).annotate( num_books=Count('book') )

#Publisher反向外键引用Book过滤至少有一本书评分高于3分的出版商然后对每个Publisher集合计算Count(‘book’)增加注释列num_books,注意到此时如果annotate子句与filter子句引用了相同的表”,那么annotate()是在filter()过滤后的引用的表的基础上操作的也就是只对外键引用的Book记录评分高于3分的记录进行Count()集合计算

>>> a, a.num_books

(<Publisher: A>, 2)

>>> b, b.num_books

(<Publisher: B>, 1)

#~~~~~~~~~~

Both queries return a list of publishers that have at least one book with a rating exceeding 3.0, hence publisher C is excluded.

两个查询都返回至少有一本书获得超过3星以上评分的出版商的列表,因为出版商C被排除了。

In the first query, the annotation precedes the filter, so the filter has no effect on the annotation. distinct=True is required to avoid a query bug.

在第一个查询中,注释在过滤之前,因此过滤不对注释产生影响。需要distinct = True来避免查询Bug https://docs.djangoproject.com/en/3.1/topics/db/aggregation/#combining-multiple-aggregations 

The second query counts the number of books that have a rating exceeding 3.0 for each publisher. The filter precedes the annotation, so the filter constrains the objects considered when calculating the annotation.

第二个查询为每个出版商集合统计其著作评分超过3星的著作数目。过滤在注释之前因此过滤约束了计算注释时被考虑了的对象反向外键引用的Book著作实例这里强调过滤和注释考虑了相同的模型才会产生约束

Here’s another example with the Avg aggregate:

#~~~~~~~~~

>>> a, b = Publisher.objects.annotate(avg_rating=Avg('book__rating')).filter(book__rating__gt=3.0)

>>> a, a.avg_rating

(<Publisher: A>, 4.5)  # (5+4)/2

>>> b, b.avg_rating

(<Publisher: B>, 2.5)  # (1+4)/2

 

>>> a, b = Publisher.objects.filter( book__rating__gt=3.0).annotate(avg_rating=Avg('book__rating') )

#过滤在注释之前因此过滤约束了计算注释时被考虑了的对象强调是后面注释同样考虑到了前面过滤时使用的Book实例对象过滤和注释考察了相同的模型则注释考虑的Book实例就是被filter给过滤后的

>>> a, a.avg_rating

(<Publisher: A>, 4.5)  # (5+4)/2

>>> b, b.avg_rating

(<Publisher: B>, 4.0)  # 4/1 (book with rating 1 excluded)

#~~~~~~~~~

The first query asks for the average rating of all a publisher’s books for publisher’s that have at least one book with a rating exceeding 3.0. The second query asks for the average of a publisher’s book’s ratings for only those ratings exceeding 3.0.

第一个查询计算有至少一部著作的评分超过3分的出版商的所有图书的平均得分。第二个查询只对每个出版商的那些评分超过3分的著作计算平均分(肯定也超过3分)。

It’s difficult to intuit how the ORM will translate complex querysets into SQL queries so when in doubt, inspect the SQL with str(queryset.query) and write plenty of tests.

很难凭直觉知道到ORM将会如何翻译负载查询集到SQL查询因此当你有疑惑时使用str(queryset.query)观察生成的SQL命令,并写大量的测试。

 

 

order_by()

Annotations can be used as a basis for ordering. When you define an order_by() clause, the aggregates you provide can reference any alias defined as part of an annotate() clause in the query.

增加的注释列可以被用作排序的一个基础。当你定义一个order_by()子句,你提供的集合可以引用由查询中annotate()子句定义的任何别名(相当于真的字段名)。

For example, to order a QuerySet of books by the number of authors that have contributed to the book, you could use the following query:

例如,要通过为著作作出贡献的作者的数目来排序一个查询集,你可以使用如下查询:

#~~~~~~~~

>>> Book.objects.annotate( num_authors=Count('authors') ).order_by('num_authors')

#增加的注释列可被用于排序引用像一个真实的字段一样

#~~~~~~~~

 

 

values()

Ordinarily, annotations are generated on a per-object basis - an annotated QuerySet will return one result for each object in the original QuerySet. However, when a values() clause is used to constrain the columns that are returned in the result set, the method for evaluating annotations is slightly different. Instead of returning an annotated result for each result in the original QuerySet, the original results are grouped according to the unique combinations of the fields specified in the values() clause. An annotation is then provided for each unique group; the annotation is computed over all members of the group.

通常地,注释是在每个对象的基础上生成的(为查询集生成注释列,就是为每个实例|记录生成注释值)——一个注释后的查询集将会为原始查询集中的每个对象返回一个结果。然而,当一个values()子句被用于约束返回的结果集中的列时评估注释的方式有些微的不同不是为原始查询集中的每个结果记录返回一个注释后的新的结果记录而是原始结果被根据values()子句中指定的惟一的字段的组合来分组然后为每个惟一的分组提供一个注释也就是说注释是为分组中的所有成员计算的

For example, consider an author query that attempts to find out the average rating of books written by each author:

例如,考虑一个作者查询,该查询尝试找出每个作者所写的著作们的平均得分:

#~~~~~~~~

>>> Author.objects.annotate( average_rating=Avg('book__rating') )

#Author反向多对多关系引用Book,对一对多的Book记录集合平均计算。

#~~~~~~~~

This will return one result for each author in the database, annotated with their average book rating.

这将为数据库中每个author返回一个结果,并注释以他们出的书的平均得分。

However, the result will be slightly different if you use a values() clause:

然而,如果你使用一个values()子句则结果会些微不同:

#~~~~~~~~~

>>> Author.objects.values('name').annotate( average_rating=Avg('book__rating') )

#~~~~~~~~~

In this example, the authors will be grouped by name, so you will only get an annotated result for each unique author name. This means if you have two authors with the same name, their results will be merged into a single result in the output of the query; the average will be computed as the average over the books written by both authors.

在这个栗子中authors将会由name分组因此对每个惟一的作者名你将只能得到一个注释后的结果这意味着如果有两个作者名字相同则它们的结果将会合并到查询输出中的其中一个结果中而平均将会是对两位作者写的所有书计算平均分

 

 

Order of annotate() and values() clauses

As with the filter() clause, the order in which annotate() and values() clauses are applied to a query is significant. If the values() clause precedes the annotate(), the annotation will be computed using the grouping described by the values() clause.

就像和filter()子句一样,应用到一个查询的annotate()与values()子句的顺序也很重要。如果values()子句在annotate()前面,则注释将会对由values()子句描述的分组进行计算。

然而,如果annotate()子句在values()子句前面,则会对整个查询集生成注释。在这种情况下,values()子句将会只约束在(前面annotate()的)输出中生成的字段。

例如,对前面的示例反转values()和annotate()子句的顺序:

#~~~~~~~

>>> Author.objects.annotate( average_rating=Avg('book__rating') ).values('name', 'average_rating')

#首先是Author反向多对多关系引用Book 集合计算一对多的Book记录们的得分的平均值,

#~~~~~~~

This will now yield one unique result for each author; however, only the author’s name and the average_rating annotation will be returned in the output data.

这将对每个author生成一个惟一的结果;然而,只有author的name和average_rating注释会在输出数据中返回

You should also note that average_rating has been explicitly included in the list of values to be returned. This is required because of the ordering of the values() and annotate() clause.

你应该也注意到了average_rating注释值已经被显式包含在了values()的列表中用于返回这是必须的因为values()annotate()子句的顺序

If the values() clause precedes the annotate() clause, any annotations will be automatically added to the result set. However, if the values() clause is applied after the annotate() clause, you need to explicitly include the aggregate column.

如果values()子句在annotate()子句前面任何注释将会自动添加到结果集然而如果values()子句是在annotate()子句之后应用的你需要显式包含(annotate()中集合计算了的)aggregate

 

猜你喜欢

转载自blog.csdn.net/HayPinF/article/details/108694445