【问题标题】:Django - Multiple annotations with Prefetch objectsDjango - 带有预取对象的多个注释
【发布时间】:2016-08-11 00:55:43
【问题描述】:

我有一个模型 test 有两个 m2m 字段:foobar

我正在尝试对这些相关字段的条件计数进行注释,即对满足特定条件的相关对象进行计数。在查询集之外检索此信息不是一种选择,因为我需要使用带注释的字段来对结果进行排序。

我尝试了以下方法:

1.使用预取对象

from django.db.models import Prefetch, Count

prefetch_foo = Prefetch('foo_set', queryset=foo.objects.filter(<some condition>))
prefetch_bar = Prefetch('bar_set', queryset=bar.objects.filter(<some condition>))
result = test.objects.prefetch_related(prefetch_foo, prefetch_bar).annotate(n_foo=Count('foo'), n_bar=Count('bar'))

这不起作用,因为prefetch_relatedannotate 之后应用。

2。使用条件表达式

from django.db.models import Sum, Case, When
from django.db.models.fields import IntegerField

foo_sum = Sum(Case(When(foo__<some condition>, then=1), default=0,
                        output_field=IntegerField())))
bar_sum = Sum(Case(When(bar__<some condition>, then=1), default=0,
                        output_field=IntegerField())))
result = test.objects.annotate(n_foo=foo_sum, n_bar=bar_sum)

这不起作用,因为多个 Sum 注释上的这个错误:https://code.djangoproject.com/ticket/10060

3.使用 RawSQL

sql = "SELECT SUM(CASE WHEN foo.<condition> "
      "THEN 1 ELSE 0 END) FROM app_test "
      "LEFT OUTER JOIN app_foo "
      "ON (app_test.id = foo.test_id) "
      "GROUP BY test.id"
result = test.objects.annotate(n_foo=RawSQL(sql, []))
# Same method for bar

我被困在这里,因为这会检索所有行的 SUM,但我找不到添加类似 "WHERE test.id = &lt;ID of the object the annotation corresponds to&gt;" 的方法。

有没有办法从自定义 SQL 中获取正确的单行?还是其他解决方法?

【问题讨论】:

    标签: sql django orm annotations


    【解决方案1】:

    Django 中的Count 函数现在有一个filter 参数,这应该是您正在寻找的。

    See documentation.

    在这种情况下:

    result = test.objects.annotate(n_foo=Count('foo_set', filter=Q(<some condition>)), n_bar=Count('bar_set', filter=Q(<some condition)))
    

    应该给出预期的结果。

    【讨论】:

      【解决方案2】:

      我已对您的 SQL 进行了更改。您不使用 FROM 来获取 app_test。你以前见过 SQL 子查询吗?

      sql = """
        SELECT SUM(CASE WHEN app_foo.<condition> THEN 1 ELSE 0 END) 
        FROM app_foo 
        WHERE app_test.id = app_foo.id  # app_test comes from outside the sub query.
      
        """
      result = test.objects.annotate(n_foo=RawSQL(sql, []))
      

      实际运行的查询将类似于...

      SELECT app_test.*, (
        SELECT SUM(CASE WHEN app_foo.<condition> THEN 1 ELSE 0 END) 
        FROM app_foo 
        WHERE app_test.id = app_foo.id  ) AS n_foo
      FROM app_test
      

      【讨论】:

        【解决方案3】:

        我不确定您是否可以使用 Django ORM 做到这一点(因为那个 8 年前的错误),但在 RawSQL 上使用 HAVING 应该可以。你有没有尝试过这样的事情?

        sql = """SELECT SUM(CASE WHEN app_foo.<condition> 
              THEN 1 ELSE 0 END) FROM app_test 
              LEFT OUTER JOIN app_foo 
              ON (app_test.id = app_foo.test_id) 
              GROUP BY app_test.id 
              HAVING app_test.id = <ID of the object the annotation corresponds to>"""
        

        T

        【讨论】:

        • django.db.models.FRawSQL 中不可用,因此我无法在 SQL 字符串中包含对象的属性。
        猜你喜欢
        • 2021-06-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-11-13
        相关资源
        最近更新 更多