django - 确保创建了一个相关对象,竞争条件
django - make sure one related object is created, race condition
我有一个带外键的 table。 (我这里特意用外键而不是一对一)
我有一个订单模型:
class Order:
name = models.CharField(max_length=50)
user = models.ForeignKey(User)
active = models.BooleanField(default=True)
# more fields..
而且我希望每个用户只有一个此模型的实例。
我有一个保存它的简单表单(和视图)。我有这个代码:
try:
instance = Order.objects.get(user=user)
except Order.DoesNotExist:
instance = None
form = OrderForm(instance=instance)
但我注意到如果客户端同时触发两个后续请求(注意到我使用 ajax),那么可能会创建两个实例,尽管我进行了以下验证(假设更新现有的)。
这非常重要,因为我的其他代码期望每个用户只有一个此模型的实例。我怎样才能在 Django 中执行它?我根据自己的观点尝试了以下方法:
@method_decorator(transaction.atomic, name='dispatch')
但是没用。
正如我所说,我不能在这里使用一对一字段。
您可以为用户设置unique=true
。这将确保每个用户只能下一个订单。
class Order:
name = models.CharField(max_length=50)
user = models.ForeignKey(User, unique=true)
active = models.BooleanField(default=True)
您可以使用 unique_together
和可为空的字段在数据库级别强制执行唯一性:
class Order:
name = models.CharField(max_length=50)
user = models.ForeignKey(User)
active = models.NullBooleanField(default=True)
class Meta:
unique_together = (('user', 'active'), )
诀窍是将 active
设置为 None
而不是 False
用于非活动订单。由于 NULL
值在数据库级别不被视为相等,因此用户可能有多个非活动订单,但只有一个活动订单。
我有一个带外键的 table。 (我这里特意用外键而不是一对一)
我有一个订单模型:
class Order:
name = models.CharField(max_length=50)
user = models.ForeignKey(User)
active = models.BooleanField(default=True)
# more fields..
而且我希望每个用户只有一个此模型的实例。
我有一个保存它的简单表单(和视图)。我有这个代码:
try:
instance = Order.objects.get(user=user)
except Order.DoesNotExist:
instance = None
form = OrderForm(instance=instance)
但我注意到如果客户端同时触发两个后续请求(注意到我使用 ajax),那么可能会创建两个实例,尽管我进行了以下验证(假设更新现有的)。
这非常重要,因为我的其他代码期望每个用户只有一个此模型的实例。我怎样才能在 Django 中执行它?我根据自己的观点尝试了以下方法:
@method_decorator(transaction.atomic, name='dispatch')
但是没用。 正如我所说,我不能在这里使用一对一字段。
您可以为用户设置unique=true
。这将确保每个用户只能下一个订单。
class Order:
name = models.CharField(max_length=50)
user = models.ForeignKey(User, unique=true)
active = models.BooleanField(default=True)
您可以使用 unique_together
和可为空的字段在数据库级别强制执行唯一性:
class Order:
name = models.CharField(max_length=50)
user = models.ForeignKey(User)
active = models.NullBooleanField(default=True)
class Meta:
unique_together = (('user', 'active'), )
诀窍是将 active
设置为 None
而不是 False
用于非活动订单。由于 NULL
值在数据库级别不被视为相等,因此用户可能有多个非活动订单,但只有一个活动订单。