根据计算值使用 Django ORM 更新字段

Update field with Django ORM based on computed value

我有一个基本模型:

class MyModel(Model):
    id = models.IntegerField()
    is_admin = models.BooleanField()

我想实现的是根据id值是否在某个范围内,一次更新整个table的is_admin字段的值值列表。

基本上,在原始 SQL 中,这是我想要的查询:

UPDATE my_model
SET is_admin = (id IN (1, 2, 3, 4))

如何使用 Django 的 ORM 实现此目的?

这是我到目前为止尝试过的:

from django.db.models import F, Value


admin_ids = (1, 2, 3, 4)

MyModel.objects.update(is_admin=F("id") in admin_ids)
# Resulting query is:
# UPDATE my_model SET is_admin = false

MyModel.objects.update(is_admin=F("id") in Value(admin_ids))
# TypeError: argument of type 'Value' is not iterable

MyModel.objects.filter(id__in=admin_ids).update(admin=True)
MyModel.objects.exclude(id__in=admin_ids).update(admin=False)
# it works... but can I do this in a single query instead of two?

我正在使用 Django 3.2 和 PostgreSQL。

您可以使用 CASE / WHEN 结构。 https://docs.djangoproject.com/en/3.2/ref/models/conditional-expressions/

MyModel.objects.update(
    is_admin=Case(
        When(id__in=admin_ids, then=Value(True)),
        default=Value(False)
    )
)

P.S。如果你需要很多这种查询,你可以使用下面的自定义表达式(对注释也很有用)。不过要小心,我已经让它为一个查询中断一次,这可能是由于使用了旧版本的 Django。在其他项目中,我在生产中使用它没有任何问题。

class BooleanQ(ExpressionWrapper):
    output_field = BooleanField()

    def __init__(self, *args, **kwargs):
        expression = models.Q(*args, **kwargs)
        super().__init__(expression, output_field=None)

    def as_sql(self, compiler, connection):
        try:
            return super().as_sql(compiler, connection)
        except EmptyResultSet:
            return compiler.compile(Value(False))


MyModel.objects.update(is_admin=BooleanQ(id__in=admin_ids))