不考虑并发写的情况, Django ORM 的 save 方法也不是幂等的, 而且比较容易被忽略, 比如
def f1(a):
a.balance = F('balance') + 1
a.save()
def f2(a):
a.status = 1
a.save()
a = Account.objects.create(balance=0, status=0)
with transaction.atomic():
f1(a)
...
f2(a)
最终数据库里 balance 的值是 2
因为执行 f2 的时候, a.balance 不是一个数值, 依然是一个表达式, f2 中执行 save 会再次对 balance 加 1
避免此类隐藏 bug 的方案是 1). 使用 F 函数更新 Django Model 时, save 以后强制执行一次数据库读取, 即 a.refresh_from_db(), 让 a.balance 变成一个数值 2). save 方法永远都带上 update_fields, 避免更新当前上下文以外的字段, 让 f2 方法提交的 SQL 只更新 status 的值
方案 1 是最保险的, 但个人建议是在大多数情况下, 还是采用方案 2 的编码规范