我有以下型号:
class Foo(models.Model):
pass
class Bar(models.Model):
foo = models.ForeignKey(Foo)
is_successful = models.BooleanField()
如果与foo
对象相关联的所有bar
个对象都foo
为is_successful
,我希望所有True
个对象都带有注释p>
到目前为止,我的查询集是:
foos = Foo.objects.all().annotate(all_successful=Min('bar__is_successful'))
all_successful
注释的想法是,如果所有is_successful
行的最小值为1,则所有行必须为True
(假设0
为{ {1}}和False
是1
)。所以知道我可以像这样使用查询集:
True
这在sqlite中效果很好,但在PostgreSQL中失败,因为PostgreSQL无法在布尔列上执行foo = foos[0]
if foo.all_successful == 1:
print 'All bars are successful'
else:
print 'Not all bars are successful'
聚合。我想这适用于sqlite,因为sqlite将bools视为整数,因此它可以执行聚合。
我的问题是如何在不将MIN
字段转换为is_successful
的情况下使此查询集在PostgreSQL中运行?
感谢名单
答案 0 :(得分:12)
我知道这是一个老问题,但最近我遇到了这个问题。 Django v1.8现在内置了对case / when的支持,因此你可以使用ORM而不是使用自定义SQL进行黑客攻击。
https://docs.djangoproject.com/en/1.8/ref/models/conditional-expressions/#case
Foo.objects.annotate(
all_successful=Case(
When(bar__is_successful=False, then=False),
When(bar__is_successful=True, then=True),
default=False,
output_field=BooleanField()
))
我还没有尝试过,但在最近的一个项目中,类似的东西对我有用。
答案 1 :(得分:3)
FOR DJANGO< = 1.7:获得annotation
我认为您只需使用Extra
foos = Foo.objects.extra(select={'all_successful': 'CASE WHEN COUNT(b.foo) > 0 THEN 0 ELSE 1 END FROM yourapp_bar as b WHERE b.is_successful = false and b.foo = yourapp_foo.id' })
如果您的系统正在运行 Django 1.8 + ,请关注Dav3xor answer。
答案 2 :(得分:1)
受到https://docs.djangoproject.com/en/dev/topics/db/managers/的启发我建议使用Bar类的自定义管理器而不是注释
class BarManager(models.Manager):
def get_all_successful_foos_ids(self):
from django.db import connection
cursor = connection.cursor()
cursor.execute("""
SELECT foo, COUNT(*)
FROM yourapp_bar
GROUP BY 1
WHERE is_successful = true""") # <-- you have to write the correct table name here
result_list = []
for row in cursor.fetchall():
if row[1] > 0:
result_list.append(row[0])
return result_list
class Bar(models.Model):
foo = models.ForeignKey(Foo)
is_successful = models.BooleanField()
objects = BarManager() # here I'm changing the default manager
然后,在你的代码中:
foos = foo.objects.filter(id__in=Bar.objects.get_all_successful_foos_ids())