OneToOne connection between User and Profile in Django - python

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

Related

How can I add a new database entry upon user registration with Django?

I'm starting to learn Django and have a class called Customer in my models.
class Customer(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE,
primary_key=True)
cart = models.ManyToManyField(Product)
orders = models.ManyToManyField(Order)
def __init__(self, user):
self.user = user
I'm importing django.contrib.auth to register users to the database, but I would like to also initialize a Customer object upon registration.
I first attempted to override the save() method from the UserCreationForm and initialize a Customer object there:
class UserCreationForm(forms.ModelForm):
def save(self, commit=True):
user = super(UserCreationForm, self).save(commit=False)
user.set_password(self.cleaned_data["password1"])
customer = Customer(user)
customer.save()
if commit:
user.save()
return user
But it did not seem to create a Customer object.
Alternatively, is it better to extend the User class to have the Customer class fields? I initially thought I should keep authentication separate, which is why I created the Customer class.
Might be better if you created a signal instead!
from django.db.models import signals
from django.dispatch import receiver
from django.contrib.auth.models import User
from path.to.models import Customer
#receiver(signals.post_save, sender = User)
def create_customer(sender, instance, created, *args, **kwargs):
if created:
c = Customer(...) #create your customer object
c.save()
and in apps.py, import signals to run it.

Attribute error in extending django user model?

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()

Trouble with signals when inheriting from an abstract class

My app has different sort of users (parents, teachers, students) and I created a Profile abstract class to inherit from. I'm trying to use a receiver function so that whenever a user is created, a profile is also created.
In the admin page, when I try to create a Teacher Object, I need to create a User object first, but when I create a user object, I run into an error "AttributeError: type object 'Profile' has no attribute 'objects'".
I suspect this is because Profile is an abstract class, so if a User is created, it causes an error to create a Profile? If so, what's the better way to do this? I'm just trying to manually create a Teacher object in the admin page.
#models.py
class Profile(TimeStampedModel):
user = models.OneToOneField(User, on_delete=models.CASCADE)
sex = models.CharField(max_length=1, choices=SEX)
user_type = models.CharField(max_length=100, choices=USER_TYPE)
class Meta:
abstract = True
class Teacher(Profile):
user_type = 'teacher'
#receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)

Extending a model in Django

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)

How to execute code on save in Django User model?

I would like to run some code specifically when the is_active field is changed for a Django User, similar to how the save method works for other models:
class Foo(models.Model):
...
def save(self, *args, **kwargs):
if self.pk is not None:
orig = Foo.objects.get(pk=self.pk)
if orig.is_active != self.is_active:
# code goes here
Can this be done through another model that is in one to one relation with the User model? Something like:
class Bar(models.Model):
owner = models.OneToOneField(User, on_save=?)
...
I guess I could duplicate the is_active field on the related model and then set the is_active field on the User when saving the related model. But this seems like a bit of a messy solution.
You're looking for this Signal
from django.db.models.signals import pre_save
from django.contrib.auth.models import User
def do_your_thing(sender, instance, **kwargs):
# Do something
print(instance)
pre_save.connect(do_your_thing, sender=User)

Categories