我有一个简单的模型:
class InvitationRequest(models.Model): email = models.EmailField(max_length = 255,unique = True):
class InvitationRequestForm(forms.ModelForm): class Meta: model = InvitationRequest现在,假设我以标准方式处理它:
form = InvitationRequestForm(request.POST)如果form.is_valid(): form.save()有一个竞争条件,因为验证执行一个简单的 SELECT 查询以确定是否已经存储了此类电子邮件,如果一切正常,则继续执行 form.save()行。如果并发进程在完全相同的时刻执行相同的操作,那么两个表单都将被验证,两个进程将调用 form.save()其他将导致 IntegrityError 。
处理此问题的标准方法是什么? >
我想在表单对象中有一个标准错误,所以我可以将其传递给模板,并通知用户有关问题。
我知道:
- 我可以用try / except包装所有内容,并手动添加新的错误
- 我可以用 SERIALIZABLE 事务(在MySQL中执行下一个键锁定每个选择)包装所有内容
- 我可以使用覆盖 Model._perform_unique_checks 并使使用 select_for_update (works因为下一个键锁定)
- 我可以获取表级别的独占锁定
这些解决方案中没有一个是吸引人的,我也在使用PostgreSQL,这个区域与MySQL不同。
解决方案方式是不要处理这个,因为:
如果由于某种原因,您必须确保问题不会发生,你是自己的。
我没有详细分析事件的顺序,但我认为使用SERIALIZABLE隔离级别不会真的有帮助,它只会导致 IntegrityError (或 DatabaseError )在不同的地方被提出。
覆盖 Model._perform_unique_checks 听起来像一个坏主意,你最好远离猴子修补,如果可能的话(这里它是可能)。 p>
至于使用表锁防止可能出现的错误...嗯,我不是一个大粉丝,所以我不能推荐这个。
以下是类似问题的一个很好的答案: stackoverflow/a/3523439/176186 - 我同意抓住 IntegrityError ,然后重试可能是解决问题的最简单的方法。
编辑:我发现这个: Symfony2 - 如何从表单提交后的唯一约束错误中恢复?,我同意@ pid的回答。
I have a simple model:
class InvitationRequest(models.Model): email = models.EmailField(max_length=255, unique=True)And a simple model form:
class InvitationRequestForm(forms.ModelForm): class Meta: model = InvitationRequestNow, assuming that I attempt to process it in a standard way:
form = InvitationRequestForm(request.POST) if form.is_valid(): form.save()There is a race condition because validation performs a simple SELECT query to determine whether or not such email is already stored, and if everything is fine then it proceeds to form.save() line. If there is a concurrent process that does the same at exactly the same moment, then both forms will validate and both processes will call form.save() thus one will succeed and the other will fail causing an IntegrityError.
What is the standard way to handle this?
I want to have a standard error in the form object so I can pass it on to the template and notify user about the problem.
I know that:
- I can wrap everything with try/except and add new error to my form manually
- I can wrap everything with SERIALIZABLE transaction (in MySQL as it performs next key locking fo every select then)
- I can use override Model._perform_unique_checks and make it use select_for_update (works with MySQL because of next key locking)
- I can acquire table-level exclusive lock
None of these solutions is appealing, also I am using PostgreSQL which differs from MySQL in this area.
解决方案The standard way is to NOT handle this, as:
If, for some reason, you have to be sure that the problem won't happen, you are on your own.
I haven't analyzed the sequence of events in detail but I think that using the SERIALIZABLE isolation level won't really help, it will only cause IntegrityError (or DatabaseError) to be raised in a different place.
Overriding Model._perform_unique_checks sounds to me like a bad idea, you better stay away from monkey patching if possible (and here it is possible).
As for using the table lock to prevent unlikely errors... Well, I'm not a big fan so I cannot recommend that either.
Here's a nice answer to a similar question: stackoverflow/a/3523439/176186 - I concur that catching IntegrityError and retrying is probably the easiest sane way to deal with the problem.
EDIT: I found this: Symfony2 - how to recover from unique constraint error after form submission? and I agree with @pid's answer.
更多推荐
如何避免在Django中使用独特检查的竞争条件
发布评论