【问题标题】:django TransactionManagementError when using signalsdjango TransactionManagementError 使用信号时
【发布时间】:2013-12-06 11:39:54
【问题描述】:

我有一个与 django 的用户和 UserInfo 一对一的字段。我想订阅用户模型上的 post_save 回调函数,这样我也可以保存 UserInfo。

@receiver(post_save, sender=User) 
def saveUserAndInfo(sender, instance, **kwargs):
    user = instance
    try:
        user.user_info.save()
    except:
        info = UserInfo()
        info.user = user
        info.save()

但是,当我尝试执行此操作时,我收到了 TransactionManagementError。我假设是因为用户模型尚未完成保存,我正在尝试读取 id 以将其保存到 user_info。有人知道如何正确执行此操作吗?

第二个问题。我想在创建用户后立即将 UserInfo 实例附加到用户。因此,在 post_init 上,我尝试创建一个 UserInfo 实例并将其分配给用户实例,但它不起作用,因为尚未为用户分配 pk。我假设我只需要等到 post_save (或更高版本)来创建这个实例。这是唯一的方法吗?

追溯:

File "/Users/croberts/.virtualenvs/lunchbox/lib/python2.7/site-packages/django/core/handlers/base.py" in get_response
  114.                     response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/Users/croberts/.virtualenvs/lunchbox/lib/python2.7/site-packages/django/contrib/admin/options.py" in wrapper
  430.                 return self.admin_site.admin_view(view)(*args, **kwargs)
File "/Users/croberts/.virtualenvs/lunchbox/lib/python2.7/site-packages/django/utils/decorators.py" in _wrapped_view
  99.                     response = view_func(request, *args, **kwargs)
File "/Users/croberts/.virtualenvs/lunchbox/lib/python2.7/site-packages/django/views/decorators/cache.py" in _wrapped_view_func
  52.         response = view_func(request, *args, **kwargs)
File "/Users/croberts/.virtualenvs/lunchbox/lib/python2.7/site-packages/django/contrib/admin/sites.py" in inner
  198.             return view(request, *args, **kwargs)
File "/Users/croberts/.virtualenvs/lunchbox/lib/python2.7/site-packages/django/utils/decorators.py" in _wrapper
  29.             return bound_func(*args, **kwargs)
File "/Users/croberts/.virtualenvs/lunchbox/lib/python2.7/site-packages/django/utils/decorators.py" in _wrapped_view
  99.                     response = view_func(request, *args, **kwargs)
File "/Users/croberts/.virtualenvs/lunchbox/lib/python2.7/site-packages/django/utils/decorators.py" in bound_func
  25.                 return func(self, *args2, **kwargs2)
File "/Users/croberts/.virtualenvs/lunchbox/lib/python2.7/site-packages/django/db/transaction.py" in inner
  339.                 return func(*args, **kwargs)
File "/Users/croberts/.virtualenvs/lunchbox/lib/python2.7/site-packages/django/contrib/admin/options.py" in add_view
  1129.                 self.save_model(request, new_object, form, False)
File "/Users/croberts/.virtualenvs/lunchbox/lib/python2.7/site-packages/django/contrib/admin/options.py" in save_model
  858.         obj.save()
File "/Users/croberts/.virtualenvs/lunchbox/lib/python2.7/site-packages/django/db/models/base.py" in save
  545.                        force_update=force_update, update_fields=update_fields)
File "/Users/croberts/.virtualenvs/lunchbox/lib/python2.7/site-packages/django/db/models/base.py" in save_base
  582.                                    update_fields=update_fields, raw=raw, using=using)
File "/Users/croberts/.virtualenvs/lunchbox/lib/python2.7/site-packages/django/dispatch/dispatcher.py" in send
  185.             response = receiver(signal=self, sender=sender, **named)
File "/Users/croberts/lunchbox/userinfo/models.py" in saveUserAndInfo
  83.         info.save()
File "/Users/croberts/.virtualenvs/lunchbox/lib/python2.7/site-packages/django/db/models/base.py" in save
  545.                        force_update=force_update, update_fields=update_fields)
File "/Users/croberts/.virtualenvs/lunchbox/lib/python2.7/site-packages/django/db/models/base.py" in save_base
  573.             updated = self._save_table(raw, cls, force_insert, force_update, using, update_fields)
File "/Users/croberts/.virtualenvs/lunchbox/lib/python2.7/site-packages/django/db/models/base.py" in _save_table
  654.             result = self._do_insert(cls._base_manager, using, fields, update_pk, raw)
File "/Users/croberts/.virtualenvs/lunchbox/lib/python2.7/site-packages/django/db/models/base.py" in _do_insert
  687.                                using=using, raw=raw)
File "/Users/croberts/.virtualenvs/lunchbox/lib/python2.7/site-packages/django/db/models/manager.py" in _insert
  232.         return insert_query(self.model, objs, fields, **kwargs)
File "/Users/croberts/.virtualenvs/lunchbox/lib/python2.7/site-packages/django/db/models/query.py" in insert_query
  1511.     return query.get_compiler(using=using).execute_sql(return_id)
File "/Users/croberts/.virtualenvs/lunchbox/lib/python2.7/site-packages/django/db/models/sql/compiler.py" in execute_sql
  898.             cursor.execute(sql, params)
File "/Users/croberts/.virtualenvs/lunchbox/lib/python2.7/site-packages/django/db/backends/util.py" in execute
  69.             return super(CursorDebugWrapper, self).execute(sql, params)
File "/Users/croberts/.virtualenvs/lunchbox/lib/python2.7/site-packages/django/db/backends/util.py" in execute
  47.         self.db.validate_no_broken_transaction()
File "/Users/croberts/.virtualenvs/lunchbox/lib/python2.7/site-packages/django/db/backends/__init__.py" in validate_no_broken_transaction
  365.                 "An error occurred in the current transaction. You can't "

Exception Type: TransactionManagementError at /gov/auth/user/add/
Exception Value: An error occurred in the current transaction. You can't execute queries until the end of the 'atomic' block.

【问题讨论】:

  • 请发布完整的回溯。
  • 刚刚更新了回溯。
  • 尝试将sp = transaction.savepoint() 放在try: except: 之前、transaction.savepoint_rollback(sp) 之后except: 和之前info = UserInfo()transaction.savepoint_commit(sp) 到您的finally: 关闭try: except:
  • 我遇到了同样的错误。

标签: python django django-models


【解决方案1】:

该错误是由user.user_info.save() 行引发异常引起的,并且该事务被标记为已损坏(PostgreSQL 强制回滚整个事务,或回滚到在该事务中执行任何更多查询之前存储的任何保存点)。

发生错误时可以回滚事务:

from django.db import IntegrityError, transaction

@receiver(post_save, sender=User) 
def saveUserAndInfo(sender, instance, **kwargs):
    user = instance
    try:
        with transaction.atomic():
            user.user_info.save()
    except IntegrityError:
        info = UserInfo()
        info.user = user
        info.save()

这在documentation中有详细描述。

另外,请注意,捕获所有异常类型通常不是最好的主意,因为您可能会忽略未预料到的异常。

【讨论】:

  • 这很有意义并且有效,除非我尝试从管理面板创建新用户。我使用内联将我的 User 和 UserInfo 分组在一起,我认为它正在尝试同时保存东西,所以它在 /loginView/ 列 user_id is not unique 给了我一个 IntegrityError
  • @ChaseRoberts,查看this answer。它解决了问题。
  • save() 事务不是原子的吗?每分钟有 10k 个请求,这对我的模型来说将是一个很大的变化
  • @davidnathan 这取决于。 save 本身不是,尽管视图可以是原子的。这里的重点是,由于 OP 收到错误消息,因此保存在事务中。现在,在事务transaction.atomic () 中创建一个savepoint,它会在异常时自动回滚。这是这里的用例。
猜你喜欢
  • 1970-01-01
  • 2022-01-09
  • 1970-01-01
  • 2016-06-25
  • 1970-01-01
  • 2012-07-14
  • 2012-04-14
  • 1970-01-01
  • 2012-12-01
相关资源
最近更新 更多