这是我的模特:
class MyModel(BaseModel):
no = models.PositiveIntegerField() # Human-friendly no -> counter that starts from 1 for every type
type = models.ForeignKey(MyModelType)
我在模型的no
信号中设置了pre_save
字段的值:
@receiver(pre_save, sender=MyModel)
def mymodel_pre_save(sender, instance, *args, **kwargs):
if not instance._state.adding:
return
obj_count = MyModel.objects.filter(type=instance.type).count()
instance.no = obj_count + 1
此代码的错误是它生成具有相同no
个数字的不同对象。当多个用户同时创建对象时,可能会发生这种情况。
解决问题的最佳方法是什么?将分配移动到模型的.save()
方法是否足以在高负载环境中使用?
最终,我实施了@ moooeeeep的解决方案,但将逻辑从post_save
移到了模型的save
方法:
class MyModel(BaseModel):
...
def save(self, *args, **kwargs):
adding = self.pk is None
with transaction.atomic():
super().save(*args, **kwargs)
if adding:
# Compute and set `.no`
obj_count = MyModel.objects.filter(type=self.type, id__lte=self.id).count()
self.no = obj_count
self.save(update_fields=['no'])
答案 0 :(得分:0)
您可以使用select_for_update()方法获取db currence牵引问题的实例对象。但在高负载环境中,select_for_update方法将导致等待。
解决方案1:我建议你将这个计数更改代码从pre_save移动到post_save,如果是这样,你不需要加一个,只需将新计数放到无字段。
解决方案2:不要在数据库中存储类型计数no,只需从查询中获取它或将其缓存在redisDB中。
我的英语很差,抱歉。
答案 1 :(得分:0)
我可以想象你的问题是由竞争条件造成的:
我能想象的最简单的方法是,在插入之前不要设置项目的数量,但之后。然后,通过计算id
小于或等于相应对象的id
的适当类型的项目数,计算该类型对象的计数。然后在post_save()
处理程序中更新该值。
请注意,在计算项目后删除项目时,您的计数字段可能仍会出错。如果您要允许删除项目,则可能需要更新id
大于您要删除的id
的所有计数。
总之,从Django方面完全分配计数可能是一个坏主意。考虑使用数据库功能将计数分配为默认值(从广义上讲)计算 max(count),其中类型等于新插入项的类型或零加一个。并且不用担心要删除的物品。如果您想要准确的计数,请在需要时进行计数查询。