我的模型test
包含两个 m2m 字段:foo
和bar
。
我正在尝试注释这些相关字段的条件计数,即计算满足特定条件的相关对象。在查询集之外检索此信息不是一个选项,因为我需要使用带注释的字段来对结果进行排序。
我尝试了以下内容:
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_related
后会annotate
被应用。
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 = <ID of the object the annotation corresponds to>"
之类的内容的方法。
有没有办法从自定义SQL中获取正确的单行?还是另一种解决方法?
答案 0 :(得分:1)
Django中的Count
函数现在具有一个filter
参数,它应该是您要寻找的参数。
在这种情况下:
result = test.objects.annotate(n_foo=Count('foo_set', filter=Q(<some condition>)), n_bar=Count('bar_set', filter=Q(<some condition)))
应该给出预期的结果。
答案 1 :(得分:0)
我不确定你是否可以用Django ORM做到这一点(因为那个8岁的bug)但是在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>"""
Ť
答案 2 :(得分:0)
我已对您的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