如何在模型的save()方法中处理可能的竞争条件?
例如,以下示例实现具有相关项的有序列表的模型.创建新项目时,当前列表大小用作其位置.
据我所知,如果同时创建多个项目,这可能会出错.
class OrderedList(models.Model):
# ....
@property
def item_count(self):
return self.item_set.count()
class Item(models.Model):
# ...
name = models.CharField(max_length=100)
parent = models.ForeignKey(OrderedList)
position = models.IntegerField()
class Meta:
unique_together = (('parent','position'), ('parent', 'name'))
def save(self, *args, **kwargs):
if not self.id:
# use item count as next position number
self.position = parent.item_count
super(Item, self).save(*args, **kwargs)
Run Code Online (Sandbox Code Playgroud)
我遇到过@transactions.commit_on_success()但这似乎只适用于视图.即使它确实适用于模型方法,我仍然不知道如何正确处理失败的事务.
我当前正在处理它,但它感觉更像是一个黑客而不是一个解决方案
def save(self, *args, **kwargs):
while not self.id:
try:
self.position = self.parent.item_count
super(Item, self).save(*args, **kwargs)
except IntegrityError:
# chill out, then …Run Code Online (Sandbox Code Playgroud) 我不是Django的高级用户.我在网上看到了很多不同的方法,但它们都是针对修改过的模型,或者太复杂,我无法理解.我正在重复使用UserCreationForm我的MyRegistrationForm
class MyRegistrationForm(UserCreationForm):
email = forms.EmailField(required=True)
class Meta:
model = User
fields = ('username', 'email', 'password1', 'password2')
def save(self, commit=True):
user = super(MyRegistrationForm, self).save(commit=False)
user.email = self.cleaned_data['email']
user.set_password(self.cleaned_data["password1"])
if commit:
user.save()
return user
Run Code Online (Sandbox Code Playgroud)
我很难理解或找到一种方法来检查用户输入的用户名是否已被使用.所以我只是用它来重定向到html,它说错误的用户名或密码不匹配:
def register_user(request):
if request.method == 'POST':
form = MyRegistrationForm(request.POST)
if form.is_valid():
form.save()
return HttpResponseRedirect('/accounts/register_success')
else:
return render_to_response('invalid_reg.html')
args = {}
args.update(csrf(request))
args['form'] = MyRegistrationForm()
print args
return render_to_response('register.html', args)
Run Code Online (Sandbox Code Playgroud)
这是我的注册模板(如果需要):
{% extends "base.html" %}
{% block content %}
<section>
<h2 style="text-align: center">Register</h2>
<form …Run Code Online (Sandbox Code Playgroud) 给出以下代码:
from django.db import transaction
@transaction.atomic
def viewfunc(request):
# This code executes inside a transaction.
do_stuff()
Run Code Online (Sandbox Code Playgroud)
根据我对Django 1.6中事务的理解,如果do_stuff抛出异常,比如说IntegrityError,那么事务将被正确回滚.但是,由于Django本身正在调用视图,因此不会阻止IntegrityError上升调用堆栈并导致HTTP 500错误吗?让我们假设这不是我们想要的,因为我们希望优雅地处理错误,但仍然获得回滚功能.
所以我想明显的想法是好的,不要这样做,transaction.atomic用作包含在try中的上下文管理器,除了像这里的例子一样的块:
try:
with transaction.atomic():
generate_relationships()
except IntegrityError:
handle_exception()
Run Code Online (Sandbox Code Playgroud)
精细.但是如果你想通过在db配置中设置ATOMIC_REQUEST = True来使用每个HTTP请求的事务功能,这意味着django实际上只是将transaction.atomic装饰添加到你的视图中,这将不会捕获任何异常.ATOMIC_REQUEST如何有用?为什么你想让你的数据库错误一直传播给用户?
所以我的问题是.
urls.hadler500是否应该写一个或者我应该实现一些中间件来捕获错误?