I am trying to add a new field to the built in auth user. Here is my code
from django.db import models
from django.db.models.signals import post_save
from django.dispatch import receiver
from django.contrib.auth import get_user_model
from django.contrib.postgres.fields import ArrayField
class Profile(models.Model):
user = models.OneToOneField(get_user_model(), on_delete=models.CASCADE)
newField = ArrayField(models.CharField(max_length=16))
#receiver(post_save, sender=get_user_model())
def create_user_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
#receiver(post_save, sender=get_user_model())
def save_user_profile(sender, instance, **kwargs):
instance.profile.save()
And in my view I have the following:
current_user = User.objects.get(username=request.user)
current_user.save()
And i am getting the error
Exception Type: RelatedObjectDoesNotExist
Exception Value: User has no profile.
Am i doing this wrong? I creates a profile table in the db but that didnt seem to be the issue
Its maybe because you are creating user and triggering both signal functions at a time. So maybe you can try like this:
#receiver(post_save, sender=get_user_model())
def save_user_profile(sender, instance, created, **kwargs):
if not created:
instance.profile.save()
Or better,combine both signals into one:
#receiver(post_save, sender=get_user_model())
def create_user_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
else:
instance.profile.save()
The reason is your are defining two signals with one sender. So when the user is saved, both signals will be triggered, But the second is faster and will execute right away. And because for that user no profile is created, it throws an error. Because the profile object for that user by first signal isn't created yet.
#receiver(post_save, sender=get_user_model())
def create_user_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
Note no need to perform instance.profile.save() here.
Related
I am creating an app in django in which after user model is created, via the signal I create a profile:
from django.contrib.auth.models import User
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
#receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
#receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
instance.profile.save()
But as far as I feel, this does not guarantees me that profile id always equals user's one
Is there any smart way to do this safer?
I think about creating a signal which will delete profile model after user one and opposite, but I still feel that not all cases are covered
When you are creating a separate model linked in such a way, you can add primary_key = True to your OneToOne field eg,
user = models.OneToOneField(User, on_delete=models.CASCADE, primary_key=True)
This will ensure you can use the same PK value for the User and the Profile.
It's not recommended to use the built-in django User model. It's better to create your own User model, that inherits from AbstractUser. Then override the save method:
from django.contrib.auth.models import AbstractUser
class MyUser(AbtractUser)
""" Add additional fields if you want """
def __str__(self):
return "something"
def save(self, *args, **kwargs):
Profile.objects.create(user=self)
super(MyUser, self).save(*args, **kwargs)
You can also override the delete() and other model methods, read the documentation about it here: https://docs.djangoproject.com/en/4.0/topics/db/models/#overriding-predefined-model-methods
I'm confused between why some tutorials and guides write two different post_save signal like below
#receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
#receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
instance.profile.save()
when the profile can be saved by calling the save() method in one approach itself
#receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
profile = Profile(user=instance)
profile.save()
I have the following signal for creating a profile when a new user registers and then sends an email to the admin informing them of the new user
#receiver(post_save, sender=User)
def create_user_profile(sender, **kwargs):
'''Create a profile for a new user'''
if kwargs['created']:
Profile.objects.create(user=kwargs['instance'])
#receiver(post_save, sender=User)
def notify_admin(sender, instance, created, **kwargs):
'''Notify the administrator that a new user has been added.'''
if created:
subject = 'New Registration created'
message = 'A new candidate %s has registered with the site' % instance.email
from_addr = 'no-reply#example.com'
recipient_list = ('admin#example.com', )
send_mail(subject, message, from_addr, recipient_list)
I am looking to also notify the admin when a profile is updated but am struggling to come up with a solution.
Any ideas?
Method -1
As #Ralf said, use a different signal for Profile model and check the created flag
#receiver(post_save, sender=Profile)
def profile_update_signal(sender, instance, created, **kwargs):
# Profile update signal
if not created:
# do something here <<<<<<
send_mail()
Method - 2
Override the save() method of Profile model
class Profile(models.Model):
# your fields
def save(self, *args, **kwargs):
if self.pk: # not a new object
# do something here <<<<<<
send_mail()
super(Profile, self).save(*args, **kwargs)
I have done this before and got it working fine. Is this because i'm using django2.0?
models.py
class Profile(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
....
#receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
#receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
instance.profile.save()
I get the error AttributeError: 'User' object has no attribute 'profile'. I don't get it?
To access all reverse related by ForeignKey profiles you need to use profile_set. If you need to obtain and save first related object try this:
def save_user_profile(sender, instance, **kwargs):
instance.profile_set.first().save()
But in this situation I believe you'd better use OneToOneField:
user = models.OneToOneField(User, on_delete=models.CASCADE)
which allows to create only one related profile and also will allow to this syntax:
def save_user_profile(sender, instance, **kwargs):
instance.profile.save()
I am trying to create a custom user model. This model has to extend another model and also link to built-in django User. Currently I have:
class Entity(models.Model):
name = models.CharField(max_length=256, null=True, blank=True)
class Profile(Entity):
user = models.OneToOneField(User, on_delete=models.CASCADE)
#receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
#receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
instance.profile.save()
But when I try to create a user I get an error: UNIQUE constraint failed: project_profile.entity_ptr_id.
Do I need to add another function to create entity first? Or should I structure my models in another way?
if you creating the Profile instance with User it already saved in the database if you use create method. you don't need to save seperatly.
ie the following signls is enough for saving and creating user profile instance
#receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
The OneToOne relation means that you can have only one profile per user.
Modify the code to
class Profile(Entity):
user = models.ForeignKey(User, on_delete=models.CASCADE)