Search code examples
djangodjango-modelsdjango-signals

Updating the a user profile upon user save


I'm following the 'User profile' approach to extend my User model, like so:

# models.py
class UserProfile(models.Model):
    user = models.OneToOneField(User, related_name='profile', on_delete=models.CASCADE, primary_key=True)
    my_field = models.CharField(max_length=100)

# signals.py
@receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
    if created:
        UserProfile.objects.create(user=instance)

With this approach, I have to explicitly call user.profile.save(), which to me feels clunky, as I want the profile to give the illusion it is part of the User object:

# views.py
def some_func(request):
    user = User.objects.create_user('dummy', 'dummy@dummy.com', '12345678')
    user.profile.my_field = 'hello'
    user.save()          # This does not persist the profile object...
    user.profile.save()  # ...this does

To remedy this, I've changed create_user_profile() to the following, which works:

# signals.py
@receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
   profile = UserProfile.objects.get_or_create(user=instance)
   profile.save()

Numerous examples I've encountered do not use this approach. Are there any caveats to using this approach?


Solution

  • Yes, there are a few. In the following situations the post_save signal would not be fired.

    1 If the save method does not successfully save the object (such as when an IntegrityError occurs)

    2 When you call MyModel.objects.update()

    3 When you override the save method and forget to call the superclass method.

    4 When your signal receiver hasn't been successfully registered.

    In these situations your profile wouldn't be saved.