首页 > 解决方案 > Django 条件创建

问题描述

Django ORM 是否提供有条件地创建对象的方法?

例如,假设您想使用某种乐观并发控制来插入新对象。
在某个时刻,您知道要在该表中插入的最新对象,并且您只想在此后没有插入新对象的情况下才创建一个新对象。

如果是更新,您可以根据修订号进行过滤:

updated = Account.objects.filter(
    id=self.id,
    version=self.version,
).update(
    balance=balance + amount,
    version=self.version + 1,
)

但是,我找不到任何记录在案的方式来为 a create()or save()call 提供条件。

我正在寻找可以在 SQL 查询级别应用这些条件的东西,以避免“读取-修改-写入”问题。

标签: pythondjangodjango-orm

解决方案


编辑:这不是Optimistic Lock尝试。这是对 OP 提供的代码的直接回答。


Django 提供了一种实现条件查询的方法。它还提供了以下update_or_create(defaults=None, **kwargs)快捷方式:

update_or_create方法尝试根据给定的kwargs. 如果找到匹配项,它会更新默认字典中传递的字段。

默认值中的值可以是 callables

所以我们可以尝试混合和匹配这两者以重新创建提供的查询:

obj, created = Account.objects.update_or_create(
    id=self.id,
    version=self.version,
    defaults={
        balance: Case(
            When(version=self.version, then=F('balance')+amount),
            default=amount
        ),
        version: Case(
            When(version=self.version, then=F('version')+1),
            default=self.version
        )
    }
)

查询细分:

update_or_create尝试使用id=self.idversion=self.version在数据库中检索对象。

  • 找到:对象balanceversion字段将相应地使用Case条件表达式中的值进行更新(请参阅答案的下一部分)。
  • 未找到:id=self.id将创建具有和的对象,version=self.version然后更新其balanceversion字段。

条件查询的细分:

  1. balance询问:

    • 如果对象存在,则When表达式的条件将为真,因此该balance字段将使用以下值更新:

      # Existing balance       # Added amount
         F('balance')      +        amount
      
    • 如果对象被创建,它将作为初始值balance接收。amount

  2. version询问:

    • 如果对象存在,则When表达式的条件将为真,因此该version字段将使用以下值更新:

      # Existing version        # Next Version
         F('version')      +           1
      
    • 如果对象被创建,它将作为初始值接收versionself.version它也可以是默认的初始版本,如1.0.0)。


笔记:


推荐阅读