8

我有以下型号:

class Foo(models.Model):
    pass

class Bar(models.Model):
    foo = models.ForeignKey(Foo)
    is_successful = models.BooleanField()

如果与对象关联的所有对象都具有,我想获取foo带有注释的所有对象barfoois_successfulTrue

到目前为止,我的查询集是:

foos = Foo.objects.all().annotate(all_successful=Min('bar__is_successful'))

注释的想法all_successful是,如果所有行的最小值is_successful为 1,那么它们都必须是True(假设0isFalse1is True)。所以知道我可以像这样使用查询集:

foo = foos[0]

if foo.all_successful == 1:
    print 'All bars are successful'
else:
    print 'Not all bars are successful'

这在 sqlite 中效果很好,但是在 PostgreSQL 中失败了,因为 PostgreSQL 无法MIN在布尔列上执行聚合。我猜这在 sqlite 中有效,因为 sqlite 将布尔值视为整数,因此它可以执行聚合。

我的问题是如何在不将我的is_successful字段转换为 PostgreSQL 的情况下使这个查询集在 PostgreSQL 中工作IntegerField

谢谢

4

3 回答 3

16

我知道这是一个老问题,但我最近遇到了这个问题。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()
    ))

我还没有尝试过,但是在最近的一个项目中,类似的东西对我有用。

于 2015-10-04T12:15:04.270 回答
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

于 2013-03-13T00:20:06.023 回答
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())
于 2013-03-12T23:18:40.313 回答