13

I have a one to one field with django's users and UserInfo. I want to subscribe to the post_save callback function on the user model so that I can then save the UserInfo as well.

@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() 

However, I am getting a TransactionManagementError when I try to do this. I am assuming because the user model hasn't finished saving and I am trying to read the id to save it to the user_info. Anyone know how to do this properly?

A second problem. I wanted to attach a UserInfo instance to a user as soon as the user was created. So on post_init I tried to create a UserInfo instance and assign it to the user instance but it doesn't work because the user hasn't been assigned a pk yet. I am assuming I just have to wait until post_save (or later) to create this instance. Is this the only way to do it?

Traceback:

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. 
4
  • Post full traceback, please. Commented Nov 21, 2013 at 19:57
  • Just updated with the traceback. Commented Nov 21, 2013 at 21:01
  • Try putting sp = transaction.savepoint() before try: except:, transaction.savepoint_rollback(sp) right after except: and beforeinfo = UserInfo() and transaction.savepoint_commit(sp) to your finally: close of try: except:. Commented Nov 21, 2013 at 21:05
  • I'm getting the same error. Commented Nov 21, 2013 at 21:17

1 Answer 1

24

The error is caused by the user.user_info.save() line throwing an exception, and the transaction was flagged as broken (PostgreSQL enforces rolling back either the whole transaction, or to any savepoint stored before doing any more queries inside that transaction).

You can rollback the transaction when an error occurs:

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() 

This is greatly described in the documentation.

Also, note that catching all exception types is not generally the best idea as you might silence exceptions you weren't expecting.

Sign up to request clarification or add additional context in comments.

4 Comments

This makes sense and works, except for when I try to make new users from the admin panel. I have my User and UserInfo grouped together using inlines and I think it is trying to save the thing at the same time so it gives me a IntegrityError at /loginView/ column user_id is not unique
@ChaseRoberts, check out this answer. It solves the problem.
Are save() transaction not atomic already? With 10k requests per minute this would be a big change to my model
@davidnathan it depends. save itself is not, although views can be atomic. The point here is that since the OP got the error message, the save was inside a transaction. Now, inside a transaction transaction.atomic () creates a savepoint which automatically gets rolledback on exception. This is the use case here.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.