Python 如何在Django中使用唯一检查避免竞争条件
我有一个简单的模型:Python 如何在Django中使用唯一检查避免竞争条件,python,mysql,django,postgresql,validation,Python,Mysql,Django,Postgresql,Validation,我有一个简单的模型: 班级邀请请求(models.Model): email=models.EmailField(最大长度=255,唯一性=True) 和一个简单的模型形式: 课堂邀请请求表单(forms.ModelForm): 类元: 模型=邀请请求 现在,假设我尝试以标准方式处理它: form=InvitationRequestForm(request.POST) 如果form.is_有效(): form.save() 存在竞争条件,因为验证执行一个简单的SELECT查询,以确定是否已
班级邀请请求(models.Model):
email=models.EmailField(最大长度=255,唯一性=True)
和一个简单的模型形式:
课堂邀请请求表单(forms.ModelForm):
类元:
模型=邀请请求
现在,假设我尝试以标准方式处理它:
form=InvitationRequestForm(request.POST)
如果form.is_有效():
form.save()
存在竞争条件,因为验证执行一个简单的SELECT
查询,以确定是否已存储此类电子邮件,如果一切正常,则继续执行form.save()
行。如果有一个并发进程在完全相同的时刻执行相同的操作,则两个窗体都将进行验证,并且两个进程都将调用form.save()
,因此一个进程将成功,另一个进程将失败,从而导致IntegrityError
处理此问题的标准方法是什么?
我希望表单对象中有一个标准错误,这样我就可以将其传递给模板,并将问题通知用户
我知道:
- 我可以使用try/except包装所有内容,并手动将新错误添加到表单中
- 我可以用
事务包装所有内容(在MySQL中,它会为每次选择执行下一个键锁定)SERIALIZABLE
- 我可以使用override
并 它使用模型。执行\u unique\u检查
(与MySQL配合使用,因为下一个键锁定)选择\u进行\u更新
- 我可以获得表级独占锁
这些解决方案都没有吸引力,而且我使用的PostgreSQL在这方面与MySQL不同。标准方法是不处理此问题,因为:
IntegrityError
(或DatabaseError
)
覆盖模型。\执行\u独特的\u检查
对我来说听起来是个坏主意,如果可能的话,你最好远离猴子补丁(在这里也是可能的)
至于使用表锁来防止不太可能的错误。。。嗯,我不是一个大球迷,所以我也不能推荐
对于一个类似的问题,这里有一个很好的答案:-我同意捕捉IntegrityError
并重试可能是处理这个问题最简单、理智的方法
编辑:我发现了这个:我同意@pid的答案。我同意托马斯齐林斯基的观点,即通常的做法是不担心这个问题。对于大多数用例来说,这不值得这么麻烦 如果它很重要,最好的方法可能是使用乐观并发。在这种情况下,它可能看起来像(未经测试):
SERIALIZABLE
在这里没有什么帮助。正如前面所说的,您必须准备好处理序列化失败,这意味着代码看起来与上面的代码基本相同。(不过,如果您没有强制数据库抛出异常的unique
约束,这会有所帮助。)我不是Django的大用户,但类似的内容可能会有所帮助:@michael:Ha,很好。最近Javascript太多了。。。编辑。
from django.forms.util import ErrorList
def handle_form(request)
form = InvitationRequestForm(request.POST)
try:
if form.is_valid():
form.save()
return HttpResponseRedirect(...) # redirect to success url
except IntegrityError:
form._errors['email'] = ErrorList()
form._errors['email'].append('Error msg')
return render(...) # re-render the form with errors