Django事务管理块在检查权限时以待定的COMMIT/ROLLBACK结束
每当我在一个视图中使用了 @transaction.commit_manually
装饰器的模板里检查权限时,就会出现这个错误 Transaction managed block ended with pending COMMIT/ROLLBACK
。
模板内容:
<!-- html stuff -->
{% if perms.myApp.add_table1 %}
{# show html elements #}
{% endif %}
一旦我去掉了权限检查的条件,就没有任何错误了。与权限无关的 if
条件是可以正常工作的,比如 {% if user.is_superuser %}{% endif %}
。
更新:即使权限检查不在视图渲染的模板上,而是在从中扩展的模板上,仍然会出现这个错误。
例如,在 nav_bar.html 中检查权限,而视图渲染的是扩展自 nav_bar.html 的 expense.html,也会导致同样的错误。
view.py 文件:
@transaction.commit_manually
def add_expense(request):
# do stuff here
我尝试去掉这个装饰器,结果没有任何异常,所有功能都正常。但一旦我加上这个装饰器,就会出现错误。
urls.py 文件:
# other stuff omitted
(r'^myApp/expenses/add/$', add_expense),
更新 #2:
当用户是超级用户时,也没有问题。我认为这是因为超级用户不需要检查权限,所以不会出错。
我还附上了代码:
@login_required()
@transaction.commit_manually
def add_expense(request):
request.session.set_expiry(1800)
if request.method == 'POST':
form_input = AddExpense(request.POST)
if form_input.is_valid():
try:
# after validation data is cleaned
cd = form_input.cleaned_data
# cleaned data is a dictionary
input_date = date.today()
user = request.user.username
new_record = table1.objects.create(
amount = cd['amount'],
date = cd['date_of_expense'],
username = user
)
new_record.save()
transaction.commit()
return render_to_response('forms/add_expense_success.html', context_instance=RequestContext(request))
except Exception, e:
pass
transaction.rollback()
return HttpResponse(None)
else:
return render_to_response('forms/add_expense.html', {'form': form_input},
context_instance=RequestContext(request))
else:
# loading this gives error, not sure the top part
form = AddExpense()
return render_to_response('forms/add_expense.html', {'form': form, 'page_title': '新增支出'},
context_instance=RequestContext(request))
3 个回答
我在尝试减少访问用户的权限时遇到了这个错误信息。
- 使用的是 postgresql 9.1
- python 版本是 2.7.3
- django 版本是 1.3.1
如果以超级用户(管理员角色)身份运行,就没有任何问题;但如果以普通用户(用户角色)身份运行,就会出现错误。
这个用户在所有与 django 相关的表以及数据库和模式上都有所有权限(甚至包括授予权限),但仍然会出现错误。
这可能是什么原因呢?到目前为止,我调试发现,缺少的只是超级用户的权限,除此之外,超级用户和普通用户的权限是一样的。
代码:
@login_required
@transaction.commit_manually
def sipuser_add(request, extension_id):
member = members.objects.get(nickname=request.user.username)
extension = extensions.objects.get(id=extension_id)
nickname = request.user.username
if extension.id_members.nickname == member.nickname:
from django.db import connection
cursor = connection.cursor()
secret = pwgen()
cursor.execute("SELECT func_create_sipuser('%s','%s',%s)" % (nickname, secret, extension_id) )
ret = cursor.fetchone()
logger.debug("created extension_id: %s - stored procedure 'func_create_sipuser' returned: %s" % (extension_id, ret) )
pin = 1234
timeout = 15
cursor.execute("SELECT func_create_voicemail_from_phonenumberid(%s,'%s',%s)" % (extension_id, pin, timeout) )
ret = cursor.fetchone()
logger.debug("created voicemail for extension_id %s - stored procedure 'func_create_voicemail' returned: %s" % (extension_id, ret) )
cursor.close() # is this needed and/or on the right position called?
connection.commit()
return HttpResponseRedirect('/extensions/')
我想确认一下,错误信息的意思是,你是在使用Postgres,对吧?
如果是这样,我有一个建议:在你上面提到的# do stuff here
这部分代码里,我敢打赌你在读取数据后没有手动提交,即使在写入数据后有提交。
从Django 1.3开始,读取数据的事务管理也会被算作脏数据,不仅仅是写入数据,所以你在读取后也需要调用commit()
。
想了解更多细节,可以查看发布说明。