我有这个型号:
class Invoice(models.Model):
owner = models.ForeignKey(settings.AUTH_USER_MODEL)
data = models.TextField(default=None, blank=True, null=True)
number = models.PositiveIntegerField(default=0, null=False)
我需要的是为每个单独的用户自动增加字段
number
。理由是每个用户都有一个 Invoice
列表,从 number=1
到 number=latest.number+1
。
我确实知道
F()
表达式,但不知道如何为每个特定用户引用最新/最好的 number
。也许 Invoice.objects.filter(owner=request.user).aggregate(Max('number'))
是路径,但是我如何确保 Max()
和 F()
之间不存在竞争条件?
您可以通过重写模型中的保存方法并向其写入自定义逻辑来实现此功能和类似功能。
class Invoice(models.Model):
owner = models.ForeignKey(settings.AUTH_USER_MODEL)
data = models.TextField(default=None, blank=True, null=True)
number = models.PositiveIntegerField(default=0, null=False)
def save(self, *args, **kwargs):
if self.pk:
self.number += 1
# Write all your logic here, like handeling max value etc
return super(Invoice, self).save(*args, **kwargs)
你可以像这样得到你的第一个或最后一个对象:
# For last Object
Model.objects.latest('field') # Field can be id or pk or ...
# For first Object
Model.objects.all().first() # You can also use it on filter
一个简单的解决方案是您可以将 number 字段设为主键,因为它的性质是相似的。
class Invoice(models.Model):
owner = models.ForeignKey(settings.AUTH_USER_MODEL)
data = models.TextField(default=None, blank=True, null=True)
number = models.IntegerField(primary_key=True)
或者,您可以将 number 设置为 AutoField 或 BigAutoField。
number = models.AutoField()
在这样的事务中使用 select_for_update()
with transaction.atomic():
latest_invoice = Invoice.objects.filter(owner=user).select_for_update().aggregate(Max('number'))
max_number = latest_invoice['number__max']
next_number = (max_number or 0) + 1
invoice = Invoice.objects.create(owner=user, data=data, number=next_number)
可以在这里找到相关文档:- https://docs.djangoproject.com/en/5.1/ref/models/querysets/#select-for-update