Django: extend User model with profile onetoonefield errors - django

I have this model
from django.contrib.auth.models import User
from django.db import models
from django.db.models.signals import post_save
from django.dispatch import receiver
from my_app.models import Teams
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
team = models.ForeignKey(Teams, on_delete=models.CASCADE)
class Meta:
app_label = 'my_app'
def __str__(self):
return self.name
#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 then go into the shell
from django.contrib.auth.models import User
when I type
user = User.objects.create_user(username='test', email='dummyemail#dum.com', password='test')
I unsurprisingly get
IntegrityError: NOT NULL constraint failed: my_app_profile.team_id
but when I type
user = User.objects.create_user(username='test', email='dummyemail#dum.com', password='test', team='developer')
I get
TypeError: 'team' is an invalid keyword argument for this function
If I type
user = User.objects.create_user(username='test', email='dummyemail#dum.com', password='test', profile.team='developer')
I get
SyntaxError: keyword can't be an expression
Any help, hints or guidance would be greatly appreciated

You haven't allowed team to be null, so when you create a profile like so:
#receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
# Team isn't defined
Profile.objects.create(user=instance)
You will get an error.
Allow team to be null (or set a default in the code above):
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
team = models.ForeignKey(Teams, on_delete=models.CASCADE, null=True)
class Meta:
app_label = 'my_app'
def __str__(self):
return self.name

When you run
User.objects.create_user(username='test', email='dummyemail#dum.com', password='test')
you get IntegrityError because of post_save signal. It tries to create a Profile instance but in Profile model team cannot be NULL. By default, model fields have null=False.
When you run
user = User.objects.create_user(username='test', email='dummyemail#dum.com',
password='test', team='developer')
you get error because team is a field in Profile model, not User model.
When you run
user = User.objects.create_user(username='test', email='dummyemail#dum.com',
password='test', profile.team='developer')
you get error because you cannot use . to refer attributes. You need to use __ to filter on foreign key properties. See this question for example. However, even if you use __, it will still give error because their is no field named profile in User model.
One more thing I would suggest is to combine both post_save signals into one because both have same sender.

See the documentation about ForeignKey.related_name. By default, OneToOneField have related name to join one model to an ohter, but ForeignKey don't. Just have this option and you can do user.team to access to the model related.
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
team = models.ForeignKey(Teams,
on_delete=models.CASCADE,
related_name='team',
null=True)

Related

How to trigger a function on user creation with django?

I'm new to django and I would like to know if it is possible to trigger a function after a user has been created? For user creation I use the django administration interface.
I want to assign a default theme to a user and to do this I have the following class in my models.py file
class Profile(models.Model):
user = models.OneToOneField(User, null=False, on_delete=models.CASCADE)
selected_theme = models.TextField(max_length=50, default="Clair")
This class allows me to store the theme selected by the user
I would like to use a function that assigns a theme to a user when creating it.
To do so you need to add the following lines to the model.py file in your Profile Class:
from django.db.models.signals import post_save
from django.dispatch import receiver
class Profile(models.Model):
user = models.OneToOneField(User, null=False, on_delete=models.CASCADE)
selected_theme = models.TextField(max_length=50, default="Clair")
#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()

Adding One-To-One field to the default Django user model

I'm trying to add one-to-one field to the default Django user model but
for some reason I keep getting error from the database:
django.db.utils.IntegrityError: NOT NULL constraint failed: frontend_usermodel.test_suites_id
this is models file:
from django.contrib.auth.models import User
from django.db.models.signals import post_save
from django.dispatch import receiver
from django.db import models
class TestSuite(models.Model):
id = models.IntegerField(primary_key=True)
...
...
def __str__(self):
return self.name
class Meta:
ordering = ('name',)
class UserModel(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
test_suites = models.OneToOneField(TestSuite, on_delete=models.CASCADE)
def __str__(self):
return self.email
#receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
UserModel.objects.create(user=instance)
#receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
instance.profile.save()
What can I do to solve this ?
UPDATE:
class TestSuite(models.Model):
id = models.IntegerField(primary_key=True)
user = models.ForeignKey(User, on_delete=models.CASCADE)
...
...
Your UserModel requires both fields user and test_suites, so this line:
UserModel.objects.create(user=instance)
will fail because test_suites is None and therefore the NULL constraint is violated. You have to pass also a TestSuite instance to create your UserModel instance.
Although I don't know the exact business requirements of your application, it would seem more logical to me to set the OneToOneField on the TestSuite. I can imagine a user without test suite, and I would expect only when you create a test suite, you would assign the user. By doing that, you won't need a TestSuite instance when creating a User.

How to create auto generated membership id in django

I want to create an auto-generated membership id of a user in the profile table based on the current date and username. User table has OneToOneField relationship with the profile table. So when I create a user, I have to put username in the registration form. The signals.py creates a profile row in the table for the user. I want when the profile is created it would have a membership id which is the mix of current date and username. My code is as follow:
singlas.py
from django.db.models.signals import post_save, pre_save
from .models import Ext_User
from django.dispatch import receiver
from .models import Profile
#receiver(post_save, sender=Ext_User)
def create_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
models.py
class Profile(models.Model):
user = models.OneToOneField(Ext_User, on_delete=models.CASCADE)
image = models.ImageField(default='default.jpg', upload_to='profile_pics', null=True, blank=False)
membership_id = models.CharField(max_length=50, default='', null=True, blank=True)
def __str__(self):
return f'{self.user.username} Profile'
I have got some guideline to user pre_save into signals.py but don't understand how to figure out.
You can try this
#receiver(post_save, sender=Ext_User)
def create_profile(sender, instance, created, **kwargs):
if created:
profile = Profile()
profile.membership_id = str(instance.username) + str(datetime.datetime.now())
profile.user_id = instance.pk
profile.save()
format DateTime as your desired format

I keep getting RelatedObjectDoesNotExist at /admin/login/. How do I successfully create user profiles in Django via a one to one relationship?

I'm trying to extend the built-in user and add some more information to it. I have two apps in my django project- general and user_details. Inside my user_details app, in the models.py file, I have the following...
from django.db import models
from django.db.models.signals import post_save
from django.contrib.auth.models import User
from django.dispatch import receiver
from general.models import Ward
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
firstname = models.CharField(max_length=20)
middlename = models.CharField(max_length=20)
lastname = models.CharField(max_length=20)
loc_id = models.CharField(max_length=8, unique=True)
ward = models.ForeignKey(Ward, related_name="wards", on_delete=models.CASCADE)
phone = models.CharField(max_length=10, unique=True)
address = models.CharField(max_length=255)
postal_code = models.CharField(max_length=15)
def __str__(self):
return "{} {}".format(self.user.first_name, self.user.last_name)
class Meta:
verbose_name_plural = "User details"
#receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
else:
instance.profile.save()
I have successfully done this before but today for some reason when I try to log in via the django admin, I keep getting the RelatedObjectDoesNotExist at /admin/login/, no such column: user_details_profile.id. There are no other components involved, no views or any of that. It's just models and the admin panel. I'm trying to get the model to show in the admin panel but I ran into this error every time I try to log in.
Here is the traceback http://dpaste.com/0W7653C.
Can anyone tell me what I am doing wrong please and how I can get it to work. Help.
If you define a OneToOneField that does not mean that every object of the targeted model contains an object from the referencing model. A OneToOneField in essence is a ForeignKey with a unique=True (and some extra logic to do proper naming).
Here it thus means that although every Profile has a related user, not every user has a Profile. It is possible that there are Users for which no Profile exists.
If you thus query some_user.profile there are two scenario's that can unfold:
there is a related Profile object that is the fetched, and returned; or
there is no such object, and then it raises a RelatedObjectDoesNotExist error.
There have been some proposals to return None in the latter case, but due to backwards compatibility, this will probably not be implemented in the (near) future, or implemented at all.
So you probably have a user for which there is no profile. Based on the full traceback, the error happens on the line instance.profile.save(). We can fix this by creating a profile in case there is no such profile:
from django.core.exceptions import ObjectDoesNotExist
#receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
try:
instance.profile.save()
except ObjectDoesNotExist:
Profile.objects.create(user=instance)
We thus check if the instance has a .profile that leads to a Profile object. In case it has not, it will raise an exception, and then we create one.
This is also more in the Easier to Ask Forgiveness than Permission (EAFP) spirit of Django.
Use this. It will work
from django.core.exceptions import ObjectDoesNotExist
#receiver(post_save, sender=User)
def create_profile(sender, instance, created, **kwargs):
try:
instance.profile.save()
except ObjectDoesNotExist:
Profile.objects.create(user=instance)
Just in case someone else might face this same problem: Try creating a python file in the app of your project that's implementing the registration or sign up and call it "signals.py" and add the following code snippet to it. It worked for me perfectly.
from django.db.models.signals import post_save
from django.contrib.auth.models import User
from django.dispatch import receiver
from .models import Profile
#receiver(post_save, sender = User)
def create_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user = instance
def save_profile(sender, instance, **kwargs):
instance.profile.save()

This model works, but I do not understand how the signal works

I have the following in model.py:
from django.db import models
from django.contrib.auth.models import User
from django.db.models.signals import post_save
from django.dispatch import receiver
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
university = models.CharField(max_length=30, blank=True)
birth_date = models.DateField(null=True, blank=True)
ROLE = (
('CUSTOMER', 'User'), # (value to be set on model, human readable value)
('WORKER', 'Worker'),
)
role = models.CharField(max_length = 20, choices = ROLE, default = 'USER')
def __str__(self):
return self.user.username
#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 also have a corresponding form that, when filled out and sumbitted, saves to the database as a Profile properly.
What I do not understand is instance.profile.save() how does this work? To me it appears it should be instance.Profile.save() since, Profileexists. I am not sure where this lowercase profileis coming from?
In Django, reverse accessors are defined with the name of the related model in lowercase by default.
As your Profile model has a one-to-one relationship with User model, you can access profile instance belonging to a User instance (say user) as user.profile.
You can override this naming with a parameter where you define the OneToOneField in your model definition with the keyword argument related_name.
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='myprofile')
Now, you access via user.myprofile
See Django documentation here
By the way, be careful when you are accessing profile of a user via User instance. If there is no Profile record associated with that User instance, a DoesNotExist exception is raised.