Django 条件创建
Django conditional create
Django ORM 是否提供有条件地创建对象的方法?
例如,假设您想使用某种乐观并发控制来插入新对象。
在某个时间点,您知道要插入到那个 table 中的最新对象,并且您只想在从那时起没有插入新对象的情况下才创建一个新对象。
如果是更新,您可以根据修订号进行过滤:
updated = Account.objects.filter(
id=self.id,
version=self.version,
).update(
balance=balance + amount,
version=self.version + 1,
)
但是,我找不到任何记录的方式来为 create()
或 save()
调用提供条件。
我正在寻找可以在 SQL 查询级别应用这些条件的东西,以避免 "read-modify-write" 问题。
除了 QuerySet.update
返回受影响的行数 Django 不提供任何原语来处理乐观锁定。
然而,有一些第三方应用程序提供了这样的功能。
django-concurrency
这是最受欢迎的选项,它同时提供数据库级别的约束和应用程序级别的约束
django-optimistic-lock
不太受欢迎,但我在过去的项目中尝试过,效果很好。
django-locking
未维护。
编辑:看来 OP 毕竟不是乐观锁定解决方案。
编辑: 这不是 Optimistic Lock
尝试。这是对 OP 提供的代码的直接回答。
Django 提供了一种实现 conditional queries. It also offers the update_or_create(defaults=None, **kwargs)
快捷方式的方法,其中:
The update_or_create
method tries to fetch an object from the database based on the given kwargs
. If a match is found, it updates the fields passed in the defaults dictionary.
The values in defaults can be 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.id
和 version=self.version
的对象。
- Found: 对象的
balance
和 version
字段将相应地更新为 Case
条件表达式中的值(参见答案的下一部分)。
- 未找到: 将创建具有
id=self.id
和 version=self.version
的对象,然后它将获得其 balance
和 version
字段已更新。
条件查询的细分:
balance
查询:
如果对象存在,When
表达式的条件将为真,因此 balance
字段将更新为以下值:
# Existing balance # Added amount
F('balance') + amount
如果对象被创建,它将收到 balance
amount
值作为初始值。
version
查询:
如果对象存在,When
表达式的条件将为真,因此 version
字段将更新为以下值:
# Existing version # Next Version
F('version') + 1
如果对象被创建,它将收到 version
作为初始 self.version
值(它也可以是默认的初始版本,如 1.0.0
) .
备注:
- 您可能需要为
Case
表达式提供一个 output_field
参数,看看 here.
- 如果(双关语肯定是故意的)对
F()
表达式是什么以及它是如何使用感到好奇,我在这里有一个问答式示例:
Django ORM 是否提供有条件地创建对象的方法?
例如,假设您想使用某种乐观并发控制来插入新对象。
在某个时间点,您知道要插入到那个 table 中的最新对象,并且您只想在从那时起没有插入新对象的情况下才创建一个新对象。
如果是更新,您可以根据修订号进行过滤:
updated = Account.objects.filter(
id=self.id,
version=self.version,
).update(
balance=balance + amount,
version=self.version + 1,
)
但是,我找不到任何记录的方式来为 create()
或 save()
调用提供条件。
我正在寻找可以在 SQL 查询级别应用这些条件的东西,以避免 "read-modify-write" 问题。
除了 QuerySet.update
返回受影响的行数 Django 不提供任何原语来处理乐观锁定。
然而,有一些第三方应用程序提供了这样的功能。
django-concurrency
这是最受欢迎的选项,它同时提供数据库级别的约束和应用程序级别的约束django-optimistic-lock
不太受欢迎,但我在过去的项目中尝试过,效果很好。django-locking
未维护。
编辑:看来 OP 毕竟不是乐观锁定解决方案。
编辑: 这不是 Optimistic Lock
尝试。这是对 OP 提供的代码的直接回答。
Django 提供了一种实现 conditional queries. It also offers the update_or_create(defaults=None, **kwargs)
快捷方式的方法,其中:
The
update_or_create
method tries to fetch an object from the database based on the givenkwargs
. If a match is found, it updates the fields passed in the defaults dictionary.The values in defaults can be 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.id
和 version=self.version
的对象。
- Found: 对象的
balance
和version
字段将相应地更新为Case
条件表达式中的值(参见答案的下一部分)。 - 未找到: 将创建具有
id=self.id
和version=self.version
的对象,然后它将获得其balance
和version
字段已更新。
条件查询的细分:
balance
查询:如果对象存在,
When
表达式的条件将为真,因此balance
字段将更新为以下值:# Existing balance # Added amount F('balance') + amount
如果对象被创建,它将收到
balance
amount
值作为初始值。
version
查询:如果对象存在,
When
表达式的条件将为真,因此version
字段将更新为以下值:# Existing version # Next Version F('version') + 1
如果对象被创建,它将收到
version
作为初始self.version
值(它也可以是默认的初始版本,如1.0.0
) .
备注:
- 您可能需要为
Case
表达式提供一个output_field
参数,看看 here. - 如果(双关语肯定是故意的)对
F()
表达式是什么以及它是如何使用感到好奇,我在这里有一个问答式示例: