在 Django 的 ORM 中使用带有 UPDATE 的子查询

Posted

技术标签:

【中文标题】在 Django 的 ORM 中使用带有 UPDATE 的子查询【英文标题】:Using subqueries with UPDATE in Django's ORM 【发布时间】:2021-09-25 14:36:37 【问题描述】:

假设我有两个模型,我将它们称为 ModelAModelB。两种模型都有一些共同的字段(由field_onefield_twofield_three 表示)。此外,ModelB 有一个指向 ModelA 的外键。

class ModelA(Model):
    field_one = models.IntegerField()
    field_two = models.TextField()
    field_three = models.BooleanField()

class ModelB(Model):
    field_one = models.IntegerField()
    field_two = models.TextField()
    field_three = models.BooleanField()
    model_a = models.ForeignKey(ModelA, on_delete=models.CASCADE)

我需要更新ModelB 的所有实例以将字段的值更新为关联ModelA 实例的值。我需要完全在数据库中执行此操作,而不需要实例化任何模型实例(不使用.save()bulk_update())。

我知道如何在 PostgreSQL 中使用子查询来实现这一点:

UPDATE model_b SET (field_one, field_two, field_three) =
    (SELECT field_one, field_two, field_three FROM model_a
     WHERE model_b.model_a_id = model_a.id);

如何在 Django 的 ORM 中表达上述查询?


这是我能得到的最接近的:

ModelB.objects.update(
    field_one=Subquery(ModelA.objects.filter(id=OuterRef('model_a_id')).values(field_one)[:1]),
    field_two=Subquery(ModelA.objects.filter(id=OuterRef('model_a_id')).values(field_two)[:1]),
    field_three=Subquery(ModelA.objects.filter(id=OuterRef('model_a_id')).values(field_three)[:1])
)

但是,这会导致每个字段都有一个子查询:

UPDATE model_b SET
    field_one = (SELECT model_a.field_one FROM model_a WHERE model_a.id = model_b.model_a_id LIMIT 1),
    field_two = (SELECT model_a.field_two FROM model_a WHERE model_a.id = model_b.model_a_id LIMIT 1),
    field_three = (SELECT model_a.field_three FROM model_a WHERE model_a.id = model_b.model_a_id LIMIT 1);

【问题讨论】:

本可以支持你的第一个查询:ModelB.objects.update(field_one=F('modela__field_one'), field_two=F('modela__field_two'), field_three=F('modela__field_three')) ,但不幸的是,django 不支持这个。见herehere 所以如果你想在一个查询中完成,我认为子查询是唯一的选择。 【参考方案1】:

不幸的是,ORM 不支持分散多列的注释,我不知道它的功能请求。

如果你想坚持使用 ORM,你将不得不承受可能的性能损失(也许 PostgreSQL 足够聪明,只能使用一个;EXPLAIN 会告诉你)否则you'll have to switch to .raw SQL。

【讨论】:

【参考方案2】:

通过在modelB类参数中传递modelA作为参数,ModelB和modelC将继承modelA的每个字段

class ModelA(Model):
    field_one = models.IntegerField()
    field_two = models.TextField()
    field_three = models.BooleanField()

class ModelB(ModelA):
    pass

class ModelC(ModelA):
    pass

【讨论】:

谢谢,但这与我要问的特定问题无关。表结构在这里是不可协商的。

以上是关于在 Django 的 ORM 中使用带有 UPDATE 的子查询的主要内容,如果未能解决你的问题,请参考以下文章

Django ORM:带有后续过滤的窗口函数

带有连接的 Django ORM SELECT

带有来自模型的数据库的 PHP Orm

带有排除的 Django ORM 查询无法正常工作

django orm 和 postgresql 的累积(运行)总和

带有 Postgres 的 ASP.NET MVC; ORM 推荐?