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', '[email protected]', '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?
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.