Django IntegrityError on profile creation - django

Why am I getting the Integrity Error despite the fact that I'm checking that the username is unique here:
(I also tried try/expect IntegretyError instead of e.count())
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
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
from slugify import slugify
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name="profile")
username = models.CharField(max_length=30, blank=True, unique=True) #should be true on signup
name = models.CharField(max_length=30, blank=True)
bio = models.TextField(max_length=500, blank=True)
location = models.CharField(max_length=30, blank=True)
email = models.EmailField() #should be true on signup
avatar_url = models.URLField(default="https://image.flaticon.com/icons/png/512/64/64572.png")
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(commit=False)
from allauth.account.signals import user_logged_in, password_set, user_signed_up
from django.db.models.signals import post_save
from django.dispatch import receiver, Signal
#receiver(user_logged_in)
def populate_profile(sociallogin, user, **kwargs):
# picture_url = ""
if sociallogin.account.provider == 'github':
user_data = user.socialaccount_set.filter(provider='github')[0].extra_data
print(user_data)
username = user_data['login']
avatar_url = user_data['avatar_url']
email = user_data['email']
name = user_data['name']
bio = user_data['bio']
location = user_data['location']
if sociallogin.account.provider == 'twitter':
user_data = user.socialaccount_set.filter(provider='twitter')[0].extra_data
print(user_data)
username = user_data['screen_name']
avatar_url = user_data['profile_image_url'].replace("_normal", "")
email = user_data['email']
name = user_data['name']
bio = user_data['description']
location = user_data['location']
e = Profile.objects.filter(username=username)
if e.count() > 0:
user.profile.username = slugify(name)
else:
user.profile.username = username
#except IntegrityError:
user.profile.avatar_url = avatar_url
user.profile.email = email
user.profile.name = name
user.profile.bio = bio
user.profile.location = location
user.profile.save()

Well, probably this line of code is the issue:
#receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
# if created:
Profile.objects.create(user=instance) # <---
Because, every-time user is saved, this signal will be triggered, hence will try to create a profile. Instead, use the commented out code section:
#receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
Then only if the a user is created, then a profile will be created.
Also I am not sure if the following code will work. Because model's save() method does not have any keyword argument named commit. So it might throw error.
To be honest, you don't need that signal, so you can remove that as well.

Related

Overwriting save method to create entry in related table automatically django

After registration email with email confirmation is sent to a new user. I created model
UserWithConfirmation with new field is_email_confirmed. I was following this https://docs.djangoproject.com/en/4.1/topics/auth/customizing/#extending-the-existing-user-model.
I want to have UserWithConfirmation created for each new user when user is saved. For now I have sth like this.
from django.db import models
from django.contrib.auth.models import User
class UserWithConfirmation(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name="user_with_confirmation")
is_email_confirmed = models.BooleanField(default=False)
def __str__(self):
return self.user.username
class User:
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
create_user_with_confirmation(User)
def create_user_with_confirmation(user):
UserWithConfirmation(user=user)
UserWithConfirmation.save()
How to make it works?
Just have UserWithConfirmation extend User
class UserWithConfirmation(User):
is_email_confirmed = models.BooleanField(default=False)
and change the entry when the email is confirmed
I solved my problem using signals
I changed UserWithConfirmation to Profile
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name="profile")
is_email_confirmed = models.BooleanField(default=False)
#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()

Django not null constraint failed when form is submitted

I use the basic user model, and i'd like to have a profile connected to it aswell. But when I submit my form i get this error: NOT NULL constraint failed: users_profile.user_id
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.OneToOneField(User, on_delete=models.CASCADE, unique=True)
weight = models.FloatField(max_length=20, blank=True, null=True)
height = models.FloatField(max_length=20, blank=True, null=True)
goal = models.FloatField(max_length=20, blank=True, null=True)
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()
Views:
from django.shortcuts import render
from django.contrib import messages
from users import models
from users.models import Profile
from .forms import WeightForm
# Create your views here.
def home(request):
form = WeightForm()
if request.method == 'POST':
form = WeightForm(request.POST)
if form.is_valid:
form.save()
return render(request, 'Landing/index.html', {'form': form})
The problem is that your "update" case is running before the create case. Instead of having two receivers, use one with a condition on created.
#receiver(post_save, sender=User)
def save_user_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
else:
instance.profile.save()

Add more field in SignupForm using django-allauth

Models:
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
bio = models.TextField(max_length=500, blank=True)
nationality = models.CharField(max_length=20)
def __str__(self):
return self.user.first_name
#receiver(post_save, sender=User)
def create_user_profile(self, sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
#receiver(post_save, sender=User)
def save_user_profile(self, sender, instance, **kwargs):
instance.profile.save()
Forms:
from allauth.account.forms import SignupForm
class CustomSignupForm(SignupForm):
first_name = forms.CharField(max_length=100)
last_name = forms.CharField(max_length=100)
class Meta:
model = Profile
fields = ('first_name', 'last_name', 'nationality', 'bio')
def signup(self, request, user):
# Save your user
user.first_name = self.cleaned_data['first_name']
user.last_name = self.cleaned_data['last_name']
user.save()
user.profile.nationality = self.cleaned_data['nationality']
user.profile.gender = self.cleaned_data['bio']
user.profile.save()
Views:
ACCOUNT_FORMS = {'signup': 'myproject.forms.CustomSignupForm',}
This process isn't work. Error is: Model class all_auth.models.Profile doesn't declare an explicit app_label and isn't in an application in INSTALLED_APPS.
How can I solve it? Or, How can i add more field with SignupForm using django-allauth?
Create an application, such as accounts and it has this code, but you need to create a database only after creating this code, it is more accurate to perform the first migration in the project
accounts/models.py
from django.db import models
from django.contrib.auth.models import AbstractUser
class CustomUser(AbstractUser):
phone = models.CharField(max_length=12)
accounts/forms.py
from allauth.account.forms import SignupForm
from django import forms
from .models import *
class SimpleSignupForm(SignupForm):
phone = forms.CharField(max_length=12, label='Телефон')
def save(self, request):
user = super(SimpleSignupForm, self).save(request)
user.phone = self.cleaned_data['phone']
user.save()
return user
settings.py
...
ACCOUNT_FORMS = {'signup': 'accounts.forms.SimpleSignupForm'}
AUTH_USER_MODEL = 'accounts.CustomUser'
accounts/admin.py
from django.contrib import admin
from .models import *
admin.site.register(CustomUser)

django: extending user model with one-to-one link - how to use post_save signal

I am new to django , I am learning how to extend the user model using the following site:
https://simpleisbetterthancomplex.com/tutorial/2016/07/22/how-to-extend-django-user-model.html
I have some doubt in the following model:
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)
bio = models.TextField(max_length=500, blank=True)
location = models.CharField(max_length=30, blank=True)
birth_date = models.DateField(null=True, blank=True)
#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()
How this line exactly works:
Profile.objects.create(user=instance)
I know post_save will be called after user is created, but I don't understand how the following line is used to store the additional fields.
Profile.objects.create(user=instance)

Saving a extended user profile

I need save additional information about users when they register.
I used this:
https://docs.djangoproject.com/en/dev/topics/auth/#storing-additional-information-about-users
, but I stuck. Relation are created, but the field key is empty.
models.py
from django.db import models
from django.contrib.auth.models import User
from django.db.models.signals import post_save
class UserProfile(models.Model):
user = models.OneToOneField(User, unique=True)
key = models.CharField(max_length=20, null=True, blank=True)
def __unicode__(self):
return u'%s' % self.user
def create_user_profile(sender, instance, created, **kwargs):
if created:
UserProfile.objects.create(user=instance)
post_save.connect(create_user_profile, sender=User)
views.py
def registration(request):
form = RegistrationForm(request.POST or None)
if form.is_valid():
first_name = form.cleaned_data['first_name']
last_name = form.cleaned_data['last_name']
email = form.cleaned_data['email']
password = form.cleaned_data['password']
user = User.objects.create_user(login, email, password)
user.first_name = first_name
user.last_name = last_name
user.is_active = False
# I've tried both ways, but it not write anything in to the table
# user.key = ''.join(random.choice(string.digits) for i in range(12))
# user.get_profile().key = ''.join(random.choice(string.digits) for i in range(12))
user.save()
Thanks.
profile = user.get_profile()
profile.key = ''.join(random.choice(string.digits) for i in range(12))
profile.save()
user.save()
This is the correct way to do it. You have to save the instance of the profile object as well as the user object
You can also try this,
#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()