Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I prevent fixtures from conflicting with django post_save signal code?

In my application, I want to create entries in certain tables when a new user signs up. For instance, I want to create a userprofile which will then reference their company and some other records for them. I implemented this with a post_save signal:

def callback_create_profile(sender, **kwargs):     # check if we are creating a new User     if kwargs.get('created', True):         user = kwargs.get('instance')         company = Company.objects.create(name="My Company")         employee = Employee.objects.create(company=company, name_first=user.first_name, name_last=user.last_name)         profile = UserProfile.objects.create(user=user, employee=employee, partner=partner) # Register the callback post_save.connect(callback_create_profile, sender=User, dispatch_uid="core.models") 

This works well when run. I can use the admin to create a new user and the other three tables get entries with sensible as well. (Except that is, the employee since the user.first_name and user.last_name aren't filled out in the admin's form when it saves. I still don't understand why it is done like that)

The problem came when I ran my test suite. Before this, I had created a bunch of fixtures to create these entries in the tables. Now I get an error that states:

IntegrityError: duplicate key value violates unique constraint "core_userprofile_user_id_key" 

I think this is because I have already created a company,employee and profile records in the fixture with id "1" and now the post_save signal is trying to recreate it.

My questios are: can I disable this post_save signal when running fixtures? Can I detect that I am running as part of the test suite and not create these records? Should I delete these records from the fixtures now (although the signal only sets defaults not the values I want to be testing against)? Why doesn't the fixture loading code just overwrite the created records?

How do people do this?

like image 998
poswald Avatar asked Aug 17 '10 06:08

poswald


People also ask

What is the use of the Post_delete signal in Django?

Django Signals - post_delete()To notify another part of the application after the delete event of an object happens, you can use the post_delete signal.

Are Django signals synchronous?

Like most of Django, they are fully "synchronous".

What are signals in Django what all the methods available?

There are 3 types of signal. pre_save/post_save: This signal works before/after the method save(). pre_delete/post_delete: This signal works before after delete a model's instance (method delete()) this signal is thrown. pre_init/post_init: This signal is thrown before/after instantiating a model (__init__() method).


2 Answers

I think I figured out a way to do this. There is a 'raw' parameter in the kwargs passed in along with signals so I can replace my test above with this one:

if (kwargs.get('created', True) and not kwargs.get('raw', False)): 

Raw is used when loaddata is running. This seems to do the trick.

It is mentioned here: http://code.djangoproject.com/ticket/13299

Would be nice if this was documented: http://docs.djangoproject.com/en/1.2/ref/signals/#django.db.models.signals.post_save

like image 77
poswald Avatar answered Sep 24 '22 00:09

poswald


This is an old question, but the solution I've found most straightforward is to use the 'raw' argument, passed by load data, and decorate the listener functions, for example:

from functools import wraps   def disable_for_loaddata(signal_handler):     @wraps(signal_handler)     def wrapper(*args, **kwargs):         if kwargs['raw']:             print "Skipping signal for %s %s" % (args, kwargs)             return         signal_handler(*args, **kwargs)     return wrapper 

and then

@disable_for_loaddata def callback_create_profile(sender, **kwargs):     # check if we are creating a new User     ... 
like image 22
bjw Avatar answered Sep 22 '22 00:09

bjw