Duplicate key error when saving a new user - python

I'm trying to save a User and a Profile in django which are linked together using a oneToOneField but I'm getting an error saying
duplicate key value violates unique constraint
"auth_user_username_key
eventhough I dont have any duplicates.
I also get this error:
duplicate key value violates unique constraint
"api_profile_user_id_key" DETAIL: Key (user_id)=(9) already exists.
Here is my code:
model.py
from django.db import models
from django.contrib.auth.models import User
from django.db.models import CharField, OneToOneField
from django.db.models.signals import post_save
from django.dispatch import receiver
class Profile(models.Model):
user = OneToOneField(User, on_delete=models.CASCADE)
phone_number = CharField(max_length=20)
account_type = CharField(max_length=10, default='basic')
facebook_id = CharField(max_length=20, blank=True)
google_id = CharField(max_length=20, blank=True)
notification_id = CharField(max_length=40, blank=True)
# TODO add account info and watchlist
def __str__(self):
return self.user.email
#receiver(post_save, sender=User)
def user_save(sender, instance, **kwargs):
Profile.objects.create(user=instance)
view.py:
#api_view(['POST'])
def sign_up(request):
data = request.data
user = User.objects.create_user(username=data['username'],
password=data['password'],
first_name=data['first_name'],
last_name=data['last_name'],
email=data['email']
)
user.profile.phone_number = data['phone_number']
user.save()
return Response('hey')
I think the problem is in the post_save receiver however I'm not sure.

create_user is also calling save(), then you also call user.save(), the post_save signal is being called twice for one user, you get the error because two Profile objects are being created for one user
You could do:
#receiver(post_save, sender=User)
def user_save(sender, instance, **kwargs):
Profile.objects.get_or_create(user=instance)

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.

Save multiple values in one field (Django)

The problem:
I have a model, which is referencing the basic User model of django. Right now, if I submit the form Django updates my database by replacing the existing data with the new one. I want to be able to access both of them. (In weight and date field)
Models file:
I saw other posts here, where they solved a problem by specifying a foreign key, but that doesn't solve it for me.
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
# Create your models here.
class Profile(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
weight = models.FloatField(max_length=20, blank=True, null=True)
height = models.FloatField(max_length=20, blank=True, null=True)
date = models.DateField(auto_now_add=True)
def __str__(self):
return self.user.username
#receiver(post_save, sender=User)
def save_user_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
Views file:
This is where I save the data that I get from my form called WeightForm
from django.shortcuts import render
from django.contrib.auth.models import User
from django.http import JsonResponse
from django.shortcuts import get_object_or_404
from users import models
from users.models import Profile
from .forms import WeightForm
def home(request):
form = WeightForm()
if request.is_ajax():
profile = get_object_or_404(Profile, id = request.user.id)
form = WeightForm(request.POST, instance=profile)
if form.is_valid():
form.save()
return JsonResponse({
'msg': 'Success'
})
return render(request, 'Landing/index.html',{'form':form})
What I tried:
I used to have a OneToOneField relation with this model, but as you can see I changed it to foreignkey, according to answers I saw on this site.
Thanks if you've gotten this far in my mess :D
I didn't understood exactly what you mean by "I want to be able to access both of them. (In weight and date field)" but I guess you want user to be able to see their previous data of weight and Date also, so you can try doing this:
In your models.py do try doing this,
class Profile(models.Model):
user_id = models.AutoField(primary_key=True)
user = models.ForeignKey(User, on_delete=models.CASCADE)
height = models.FloatField(max_length=20, blank=True, null=True)
def __str__(self):
return self.user.username
class UserData(models.Model):
Data_id = models.AutoField(primary_key=True)
user_id = models.ForeignKey(Profile, on_delete=models.CASCADE)
weight = models.FloatField(max_length=20, blank=True, null=True)
date = models.DateField(auto_now_add=True)
then u can have seperate forms for both the models and use them combined.
You can make a workaround
Create new model which would include something like "version"
Reference to version with foreign key
class ProfileChange(models.Model):
Date = models.DateField(default=datetime.datetime.today().strftime('%Y-%m-%d'))
#classmethod
def create(cls):
object = cls()
return object
class Profile(models.Model):
version = models.ForeignKey(ProfileChange,on_delete=models.CASCADE)
Unfortunately, you could see only one ProfileChange a day. If you want to see more of them, instead of models.DataField use models.IntegerField

Access Django custom user profile field

I added a custom user profile field role with the following models.py.
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
class Profile(models.Model):
ROLE_CHOICES = (
(792, 'Student'),
(172, 'Teacher'),
(864, 'Supervisor'),
)
user = models.OneToOneField(User, on_delete=models.CASCADE)
role = models.IntegerField(choices=ROLE_CHOICES, null=True, blank=True)
def __str__(self):
return self.user.username
#receiver(post_save, sender=User)
def create_or_update_user_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
instance.profile.save()
What is the best way to return the int value assigned to the role? For example if the current logged in user is assigned the role of "Teacher", I want to be able to use the 172 value elsewhere like in views.py.
Currently logged in user is request.user, so
request.user.profile.role
will return 172, and
request.user.profile.get_role_display()
will return "Teacher". Note that you should omit parentheses if you are using the latter in a template.

Delete object from database automatically if one field is TRUE

Automatically delete an object from the database if one attribute of the object is TRUE.
I've tried Django Signals, but it didn't help.
class Question(models.Model):
name = models.CharField(max_length=50)
email = models.EmailField(max_length=50)
question = models.TextField(max_length=200)
answered = models.BooleanField(default=False)
def __str__(self):
return self.name
If I change the "answered" field to TRUE in Admin Panel, then this object must be automatically deleted from the database.
You will need post_save signals by using something like:
from .models import Question
from django.db.models.signals import post_save
from django.dispatch import receiver
#receiver(post_save, sender=Question)
def save_profile(sender, instance, **kwargs):
if instance.answered:
instance.delete()

primary key must be unique when extending table

I'm trying to add extend user model. I keep getting this error: Primary key is not unique.
class UserExtended(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE,parent_link=True,primary_key=True)
If I remove primary_key=True then I get the error instance.userextended.id does not exists well, of course it doesn't since now I dont have id.
How do I get around this?
In models.py
from django.contrib.auth.models import User
from django.db.models.signals import post_save
from django.dispatch import receiver
class UserExtended(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
# Then you can also add fields like these to extended model
profilepic = models.ImageField(upload_to='uploads/users/%Y/%m/%d/', null=True, blank=True)
designation = models.CharField(max_length=200,null=True, blank=True)
about = models.TextField(null=True, blank=True)
website = models.URLField(null=True,blank=True)
def __str__(self):
return self.user.username
#receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
UserExtended.objects.create(user=instance)
#receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
instance.userextended.save()
Now, whenever a new user is created, a extended record for that user will be created automatically.
Also, if a user record is saved, then it will automatically be updated in extended record.

Categories