你真的应该发布你的模型......
但是给定一个像这样的基本结构:
from django.db import models, transaction
from django.utils import timezone
class Account(models.Model):
iban = models.CharField(max_length=34, primary_key=True)
@property
def balance(self):
return self.mutations.last().end
class AccountMutation(models.Model):
id = models.BigAutoField(primary_key=True)
account = models.ForeignKey(
Account, on_delete=models.PROTECT, related_name="mutations"
)
start = models.DecimalField(decimal_places=4, max_digits=12)
timestamp = models.DateTimeField(default=timezone.now)
amount = models.DecimalField(decimal_places=4, max_digits=12)
end = models.DecimalField(decimal_places=4, max_digits=12)
objects = AccountMutationManager()
class Meta:
ordering = ("timestamp", "pk")
我们可以这样实现自定义管理器:
class AccountMutationManager(models.Manager):
@transaction.atomic
def recalculate(self, from_: "AccountMutation"):
qs = self.filter(
account=from_.account, timestamp__gte=from_.timestamp, id__gt=from_.id
).select_for_update()
prev = from_
for mutation in qs:
mutation.start = prev.end
mutation.end = mutation.start + mutation.amount
mutation.save()
prev = mutation
当然,这是基于基于时间戳的突变来确定排序的。如果您使用前一个指针链接事务,则选择会略有不同,您需要一个 reorder() 方法以防时间戳发生更改。但这实际上取决于它的存储方式。
通常情况下,我不会使用突变存储开始和结束余额,而是将它们设为计算字段,并且每个帐户都会有每个“会计年度”的期初余额,从该开始计算。
一些说明:
- 我们对原子事务使用全有或全无的方法
- 此外,我们使用
select_for_update() 锁定所有将受到影响的行,这样就不会同时发生影响相同数据的两次重新计算。
-
from_ 是已更改并假定具有正确最终平衡的突变