Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pass additional parameters to post_save signal

Tags:

I have a user registration form in my Django application which collects additional data while a user is trying to register such as address, city, country, phone number etc.

This data is saved in the Account model class through post_save signal. The user creation process goes something like this :

# Function to Create user Account/Profile def create_user_account(sender, instance, created, **kwargs):     if created:       models.Account.objects.create(user=instance)  # Create User / User Registration def UserRegistration(request):     if request.method == 'POST':         username = request.POST['fn'].capitalize() + ' ' + request.POST['ln'].capitalize()         # CREATE USER         newuser = User.objects.create_user(username=username, email=request.POST['email'], password=request.POST['pw'])         newuser.first_name = request.POST['fn'].capitalize()         newuser.last_name = request.POST['ln'].capitalize()         newuser.save()     return HttpResponse(username)  #Post Save handler to create user Account/Profile post_save.connect(create_user_account, sender=User) 

Here the UserRegistration function is called when a user posts a form and under this function, I can get the POST data, what I want is to pass that data to create_user_account method so that it fills in the fields in the Account model.

Right now, I do see Account objects created in the database, but all the fields except the user field are empty. Obviously, because the POST variables are not being passed to the create_user_account method.

like image 388
Amyth Avatar asked Jun 24 '12 16:06

Amyth


2 Answers

What I do is to set some '_attrs' to the instance and then use them in the signal handler.

I imagine your case could be:

# Function to Create user Account/Profile def create_user_account(sender, instance, created, **kwargs):     if created:         attrs_needed = ['_language', '_field', '_otherfield']         if all(hasattr(instance, attr) for attr in attr_needed):             models.Account.objects.create(                 user=instance,                  language=instance._language,                  field=instance._field,                 otherfield=instance._otherfield)  # Create User / User Registration def UserRegistration(request):   if request.method == 'POST':     username = request.POST['fn'].capitalize() + ' ' + request.POST['ln'].capitalize()     # CREATE USER     newuser = User.objects.create_user(         username=username, email=request.POST['email'],         password=request.POST['pw'])     newuser.first_name = request.POST['fn'].capitalize()     newuser.last_name = request.POST['ln'].capitalize()      # Set some extra attrs to the instance to be used in the handler.     newuser._language = request.POST['language']     newuser._field = request.POST['field']     newuser._otherfield = request.POST['otherfield']     newuser.save()     return HttpResponse(username)  #Post Save handler to create user Account/Profile post_save.connect(create_user_account, sender=User) 

I hate to do this, and I imagine it can breaks in horrible ways, and is hard to debug sometimes, also there is no a strict way to force the data needed for the handler, one could define a signal_data(data, signal, instance) to define the data needed for the signal handler for a particular instance.

A nice option that I haven't tried is to use methods of the instance as signal's handlers and maybe we can use a more structured way to pass the data.

Bye.

like image 54
Jorge E. Cardona Avatar answered Sep 23 '22 01:09

Jorge E. Cardona


Both User.objects.create_user and User.objects.create immediately triggers the post_save handler, because the save is called in the UserManager create_user. So I cannot imagine how Jorge's answer can work (at least if you want the save to be triggered only once - why would you trigger it twice???). What you want to do is to look into what create_user does, dissect it and this way you can really control when the save is called:

# Function to Create user Account/Profile def create_user_account(sender, instance, created, **kwargs):     if created:         attrs_needed = ['_language', '_field', '_otherfield']         if all(hasattr(instance, attr) for attr in attr_needed):             models.Account.objects.create(                 user=instance,                  language=instance._language,                  field=instance._field,                 otherfield=instance._otherfield)  # Create User / User Registration def UserRegistration(request):   if request.method == 'POST':     username = request.POST['fn'].capitalize() + ' ' + request.POST['ln'].capitalize()     # CREATE USER     newuser = User(         username=username,         email=request.POST['email'],         first_name=request.POST['fn'].capitalize()         last_name = request.POST['ln'].capitalize()     )     newuser.set_password(request.POST['pw'])      # Set some extra attrs to the instance to be used in the handler.     newuser._language = request.POST['language']     newuser._field = request.POST['field']     newuser._otherfield = request.POST['otherfield']     newuser.save()  # Now this will be really the first save which is called    return HttpResponse(username)  #Post Save handler to create user Account/Profile post_save.connect(create_user_account, sender=User, weak=False) 

Note that I also use the weak=False when I hook up the handler.

like image 45
Csaba Toth Avatar answered Sep 22 '22 01:09

Csaba Toth