I want to extend the User model with custom fields, but I have a little problem
I started creating a Profiles model, with the extra fields that I want:
"models.py":
class Profiles(models.Model):
user = models.OneToOneField(User)
url = models.URLField()
avatar = models.ImageField(upload_to='profile_avatar')
def __str__(self):
return self.user.username
Then, I made a custom UserCreationForm, and I override the save function with the data that I want in the original User model:
"forms.py":
class UserForm(UserCreationForm):
first_name = forms.CharField()
last_name = forms.CharField()
email = forms.EmailField()
url = forms.URLField()
avatar = forms.ImageField()
class Meta:
model = User
fields = ("username", "first_name", "last_name", "email",)
def save(self, commit=True):
user = super(UserForm, self).save(commit=False)
user.first_name = self.cleaned_data["first_name"]
user.last_name = self.cleaned_data["last_name"]
user.email = self.cleaned_data["email"]
if commit:
user.save()
return user
Finally, I made a FormView to create the new user and save the extra data in the custom Profiles model:
"views.py":
class SignIn(FormView):
template_name = 'index/signin.html'
form_class = UserForm
success_url = reverse_lazy('signin')
def form_valid(self, form):
user = form.save()
profile = Profiles()
profile.user = user
profile.url = form.cleaned_data['url']
profile.avatar = form.cleaned_data['avatar']
profile.save()
return super(SignIn, self).form_valid(form)
The first user that I sign in with the UserForm creates correctly and the profile model is created too, but after the second sign in, Django prints this Integrity Error: '(1062, "Duplicate entry '' for key 'mail'")'; creating correctly the user, but no the profile one
Do you have any idea of what I'm doing bad?
Thank you
Related
I'm trying to save the customer field on the Test model, I'm not getting any errors but it's not saving the field either, how do I fix it?
Models
class Test(models.Model):
customer = models.ForeignKey(Customer, on_delete=models.SET_NULL, blank=True, null=True)
email = models.EmailField(max_length=200, blank=False)
Forms
class TestForm(forms.Form):
email = forms.EmailField(required=True)
class Meta:
model = Test
fields = ("email")
def save(self, commit=False):
# Creating the customer object
Test.objects.create(email=self.cleaned_data['email'])
Views
def test_view(request):
customer = request.user.customer
if form.is_valid():
email = form.cleaned_data['email']
customer = customer
form.save()
You can use cleaned_data to save the ModelForm.
forms.py
class TestForm(forms.ModelForm):
class Meta:
model = Test
fields = ["email"]
Assuming, you have request method POST.
views.py
def test_view(request):
if request.method=="POST":
form=TestForm(request.POST)
customer = request.user.customer
if form.is_valid():
email = form.cleaned_data['email']
test=Test(customer=customer,email=email)
test.save()
You need to use a ModelForm, then save the object without commiting, edit the customer of the object, then commit.
class TestForm(forms.ModelForm):
class Meta:
model = Test
fields = ["email", ]
def test_view(request):
customer = request.user.customer #I'm not sure this line is right, but I can't see all your models
if form.is_valid():
test = form.save(commit=False)
test.customer = customer
test.save()
After adding a new field to my model, it doesn't show up in the form.
I have been working on this django app and have extended the already existing User model with my own UserProfile model with extra fields. Recently, I have decided to add an extra field to the UserProfile model (profile_type). I added it to the model and included it in its corresponding form, and ran makemigrations and migrations. I then tried inserting an initial value to this field in my views and found out that it is not showing up in the form through print(), however, all the other fields are.
My UserProfile model
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete = models.CASCADE, related_name='userprofile')
gender = models.CharField(max_length = 10)
city = models.CharField(max_length = 45)
country = models.CharField(max_length = 45)
birthdate = models.DateField(null=True)
phone_number = models.CharField(max_length = 15)
profile_type = models.CharField(max_length = 6)
#receiver(post_save, sender=User)
def save_profile(sender, instance, created, **kwargs):
if created:
profile = UserProfile(user=instance)
profile.save()
My UserProfile forms (UserProfileForm contains the fields necessary for sign up, AdditionalUserProfileForm is the rest)
class UserProfileForm(UserCreationForm):
class Meta:
model = User
fields = ('first_name', 'last_name', 'email', 'username', 'password1', 'password2')
def save(self, commit=True):
user = super(UserProfileForm, self).save(commit=True)
user.email = self.cleaned_data['email']
user.first_name = self.cleaned_data['first_name']
user.last_name = self.cleaned_data['last_name']
user.save()
return user
class AdditionalUserProfileForm(forms.ModelForm):
profile_type = forms.CharField(max_length=6)
class Meta:
model = UserProfile
fields = ('gender', 'city', 'country', 'birthdate', 'phone_number', 'profile_type')
My Sign Up view (I want to add the value to profile_type manually upon instantiation
#transaction.atomic
def signup_view(request):
if request.method == 'POST':
user_profile_form = UserProfileForm(request.POST)
initial = {
'profile_type': 'user'
}
additional_user_profile_form = AdditionalUserProfileForm(request.POST, initial=initial)
valid = user_profile_form.is_valid() * additional_user_profile_form.is_valid()
if valid:
user = user_profile_form.save()
for field in ['gender', 'city', 'country', 'birthdate', 'phone_number']:
setattr(user.userprofile, field,
additional_user_profile_form.cleaned_data.get(field))
user.userprofile.save()
return redirect('login')
else:
user_profile_form = UserProfileForm()
additional_user_profile_form = AdditionalUserProfileForm()
context = {
'user_profile_form': user_profile_form,
'additional_user_profile_form': additional_user_profile_form,
}
return render(request, 'registration/signup.html', context)
Printing out the form fields in the terminal doesn't show that profile_type is one of them. I saw on some thread that it's a bug but the fix did not work for me. It was something about changing the get_fieldsets function in contrib/admin/options.py. Hope this gives you a hint. Thank you!
Am new using django so i used practical example on this tutorial on extending user model using User Profile, am having trouble on form.py it gives that error, on removing this line;
supervisor.su_mobile_number.add(*self.cleaned_data.get('su_mobile_number'))
it works smoothly but no data for su_mobile_number was inserted o the database
view.py
class SupervisorSignUpView(CreateView):
model = User
form_class = SupervisorSignUpForm
template_name = 'registration/signup_form.html'
def get_context_data(self, **kwargs):
kwargs['user_type'] = 'supervisor'
return super().get_context_data(**kwargs)
def form_valid(self, form):
user = form.save()
login(self.request, user)
return redirect('home')
model.py
class User(AbstractUser):
is_supervisor = models.BooleanField(default=False)
is_student = models.BooleanField(default=False)
class Supervisor(models.Model):
user = models.OneToOneField('User', on_delete=models.CASCADE, primary_key=True, related_name='supervisor')
su_mobile_number = models.CharField(max_length=200)
forms.py
class SupervisorSignUpForm(UserCreationForm):
su_mobile_number = forms.CharField()
class Meta(UserCreationForm.Meta):
model = User
#transaction.atomic
def save(self):
user = super().save(commit=False)
user.is_supervisor = True
user.save()
supervisor = Supervisor.objects.create(user=user)
supervisor.su_mobile_number.add(*self.cleaned_data.get('su_mobile_number'))
return user
Use = operator to assign value and then call save() method
#transaction.atomic
def save(self):
user = super().save(commit=False)
user.is_supervisor = True
user.save()
supervisor = Supervisor.objects.create(user=user)
supervisor.su_mobile_number = self.cleaned_data.get('su_mobile_number')
supervisor.save()
return user
more shorter way is (as #Abdul Niyas P M mentioned)
#transaction.atomic
def save(self):
user = super().save(commit=False)
user.is_supervisor = True
user.save()
supervisor = Supervisor.objects.create(user=user, su_mobile_number=self.cleaned_data.get('su_mobile_number'))
return user
I'm trying to add more fields to Django's default User model. I'm using the OneToOneField method. I want to create a signup page that allows the user to fill out a username, password, email, and other fields that I will add into a separate Profile model.
I've used some code snippets I've found and tried to make it work. However, I keep getting an IntegrityError at /account/signup/
UNIQUE constraint failed: accounts_profile.user_id error.
I think the problem is that right when the User gets created, a Profile gets created as well. Then When A new Profile gets created using the User's primary key it gives an error because a Profile for that primary key already exists.
Can someone show me the correct way to do this?
Here's all the relevant code:
models.py:
from django.db.models.signals import post_save
from django.dispatch import receiver
class Profile(models.Model):
GENDER_CHOICES = (
('MALE', 'Male'),
('FEMALE', 'Female')
)
user = models.OneToOneField(User, on_delete=models.CASCADE)
middle_name = models.CharField(max_length=30, blank=True, default='')
prefix = models.CharField(max_length=6, blank=True, default='')
suffix = models.CharField(max_length=10, blank=True, default='')
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()
forms.py
from django import forms
from django.forms import ModelForm
from django.contrib.auth.models import User
from django.contrib.auth.forms import UserCreationForm
from .models import Profile
class RegistrationForm(UserCreationForm):
email = forms.EmailField(required=True)
class Meta:
model = User
fields = (
'username',
'first_name',
'last_name',
'email',
'password1',
'password2',
)
def save(self, commit=True):
user = super(RegistrationForm, self).save(commit=False)
user.first_name = self.cleaned_data['first_name']
user.last_name = self.cleaned_data['last_name']
user.email = self.cleaned_data['email']
if commit:
user.save()
return user
class ProfileForm(ModelForm):
class Meta:
model = Profile
fields = (
'middle_name',
'prefix',
'suffix',
)
def save(self, commit=True):
profile = super(ProfileForm, self).save(commit=False)
profile.middle_name = self.cleaned_data['middle_name']
profile.prefix = self.cleaned_data['prefix']
profile.suffix = self.cleaned_data['suffix']
if commit:
profile.save()
return profile
views.py
...
def signup_view(request):
if request.method == 'POST':
register = RegistrationForm(request.POST, prefix='register')
userprofile = ProfileForm(request.POST, prefix='profile')
print(register.is_valid())
print(userprofile.is_valid())
if register.is_valid() * userprofile.is_valid():
user = register.save()
profile = userprofile.save(commit=False)
print(user)
profile.user = user
profile.save()
return HttpResponse('congrats')
else:
return HttpResponse('errors')
else:
userform = RegistrationForm(prefix='register')
userprofileform = ProfileForm(prefix='profile')
return render(request, 'accounts/signup.html', {'userform': userform,
'userprofileform': userprofileform})
Upon signup, I'd like to request the user for:
Full name (I want to save it as first and last name though)
Company name
Email
Password
I've read through dozens of similar situations on StackOverflow. In models.py, I extend the User model like so:
# models.py
class UserProfile(models.Model):
company = models.CharField(max_length = 50)
user = models.OneToOneField(User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
profile, created = UserProfile.objects.get_or_create(user=instance)
post_save.connect(create_user_profile, sender=User)
Source: Extending the User model with custom fields in Django
I've also added:
# models.py
class SignupForm(UserCreationForm):
fullname = forms.CharField(label = "Full name")
company = forms.CharField(max_length = 50)
email = forms.EmailField(label = "Email")
password = forms.CharField(widget = forms.PasswordInput)
class Meta:
model = User
fields = ("fullname", "company", "email", "password")
def save(self, commit=True):
user = super(SignupForm, self).save(commit=False)
first_name, last_name = self.cleaned_data["fullname"].split()
user.first_name = first_name
user.last_name = last_name
user.email = self.cleaned_data["email"]
if commit:
user.save()
return user
And in views.py:
# views.py
#csrf_exempt
def signup(request):
if request.method == 'POST':
form = SignupForm(request.POST)
if form.is_valid():
new_user = form.save()
first_name, last_name = request.POST['fullname'].split()
email = request.POST['email']
company = request.POST['company'],
new_user = authenticate(
username = email,
password = request.POST['password']
)
# Log the user in automatically.
login(request, new_user)
Right now, it doesn't store the company name. How do I do that?
user_profile = new_user.get_profile()
user_profile.company = company
user_profile.save()
Don't forget to configure your UserProfile class in settings so Django knows what to return on user.get_profile()