Django 在 BooleanField 上注释

Posted

技术标签:

【中文标题】Django 在 BooleanField 上注释【英文标题】:Django annotate on BooleanField 【发布时间】:2013-02-28 16:40:10 【问题描述】:

我有以下型号:

class Foo(models.Model):
    pass

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

如果与foo 对象关联的所有bar 对象都具有is_successful 作为True,我想获取所有带有注释的foo 对象

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

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

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

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 字段转换为 IntegerField 的情况下使这个查询集在 PostgreSQL 中工作?

感谢

【问题讨论】:

【参考方案1】:

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

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

【讨论】:

这是现在最好的方法,好点!感谢您的贡献! 您的语法比实际文档更容易理解,谢谢!缺少代码块中的最终 )。另外,请在 Case() 调用中添加最后一个 kwarg output_field=BooleanField() 所做的更改。 :)【参考方案2】:

对于 DJANGO 获得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。

【讨论】:

这太棒了。我一直在寻找一种在 Django 中使用 SQL“CASE WHEN”的好方法,结果成功了。【参考方案3】:

受到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())

【讨论】:

感谢这个想法,但这不是我想要的,因为你的建议只是过滤查询集,而我正在寻找如何注释我的 foo 模型。 我写了另一个答案,用一种更简单的方法应该给你预期的注释(我没有时间测试它,但它应该让你朝着正确的方向前进,我希望)

以上是关于Django 在 BooleanField 上注释的主要内容,如果未能解决你的问题,请参考以下文章

django 使用基于查询的聚合值注释模型

Django Booleanfield Checkbox 和 Label 分两行显示

如何在 Django 上将 True 设置为 BooleanField 的默认值?

Django BooleanField 作为单选按钮?

在 Django 中为 BooleanField 使用不同的数据库值

mysql 的 Django BooleanField 默认值