I am new to Django and creating a Django project with an account system. I extended the User model with a one-to-one UserProfile model.
However, I am having problems saving the data in the UserProfile model (saving to User model is having no problem). I tried to follow this guide to fix it but ran into AttributeError at /account/login/
'User' object has no attribute 'UserProfile' errors which is saying there are errors with instance.UserProfile.save() under models.py
How do I get the UserProfile form to save and fix this no attribute error?
models.py
from django.db import models
from django.contrib.auth.models import User
from django.dispatch import receiver
from django.db.models.signals import post_save
import datetime
from django.conf import settings
class UserProfile(models.Model):
user = models.OneToOneField(
User,
on_delete=models.CASCADE,
blank=True,
null=True,
unique=True,
related_name='userprofile'
)
bio = models.CharField(max_length=200, default='', blank=True)
campus = models.CharField(max_length=100, default='', blank=True)
website = models.URLField(default='', blank=True)
phone = models.IntegerField(default=0, blank=True)
image = models.ImageField(upload_to='profile_image', blank=True)
birthday = models.DateField(blank=True, default=datetime.date.today)
gender = models.CharField(default='', blank=True, max_length=20)
def __str__(self):
return self.user.username
#receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
UserProfile.objects.create(user=instance)
#receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
instance.UserProfile.save()
forms.py
# Edit User form
class EditProfileForm(UserChangeForm):
class Meta:
model = User
fields = (
'email',
'first_name',
'last_name',
'password',
)
# Edit User Profile form
class UserProfileForm(forms.ModelForm):
class Meta:
model = UserProfile
fields = (
'bio',
'campus',
'birthday',
'gender',
'website',
'phone',
'image',
)
views.py
#login_required
#transaction.atomic
def edit_profile(request):
if request.method == 'POST':
user_form = EditProfileForm(request.POST, instance=request.user)
profile_form = UserProfileForm(request.POST, instance=request.user.UserProfile.user)
if user_form.is_valid() and profile_form.is_valid():
user = user_form.save(commit=False)
#user.username = user.email
user.save()
profile = profile_form.save(commit=False)
profile.user = user
profile.save()
return redirect('/account/profile')
else:
profile_form = UserProfileForm(instance=request.user.userprofile)
user_form = EditProfileForm(instance=request.user)
args = {
'user_form': user_form, # basic user form
'profile_form': profile_form # user profile form
}
return render(request, 'accounts/edit_profile.html', args)
templates accounts/edit_profile.html
<form method="POST">
{% csrf_token %}
{{ user_form.as_p }}
{{ profile_form.as_p }}
<button type="submit">Submit</button>
</form>
urls.py
url(r'^profile/edit/$', views.edit_profile, name='edit_profile')
SOLUTION:
in models.py only have one receiver and change else:
instance.UserProfile().save() to else:
instance.userprofile().save()
#receiver(post_save, sender=User)
def create_or_update_user_profile(sender, instance, created, **kwargs):
if created:
UserProfile.objects.create(user=instance)
else:
instance.userprofile.save()
in views.py fix typos
change profile_form = UserProfileForm(request.POST, instance=request.user.UserProfile.user) to profile_form = UserProfileForm(request.POST, instance=request.user.userprofile)
You have two handlers listening on the same signal (post_save) and your save_user_profile function runs before create_user_profile can find a chance to create the UserProfile instance.
I would suggest consolidating both handlers into one, such as:
#receiver(post_save, sender=User)
def create_or_update_user_profile(sender, instance, created, **kwargs):
if created:
UserProfile.objects.create(user=instance)
else:
instance.userprofile.save()
Related
views.py
def register_view(request):
if request.method == 'POST':
form = UserForm(request.POST)
if form.is_valid():
user = form.save()
## login(request,user)
messages.success(request,'Account created succcessfully')
return redirect('login')
else:
messages.error(request,'Invalid information! Please retry!')
else:
form = UserForm()
context = {'form':form}
return render(request,'register.html',context)
def login_view(request):
if request.method == 'POST':
form = AuthenticationForm(data=request.POST)
if form.is_valid():
username = form.cleaned_data.get('username')
password = form.cleaned_data.get('password')
user = authenticate(username=username,password=password)
if user is not None:
login(request,user)
return redirect('homepage')
else:
messages.error(request,'No current user!')
else:
form = AuthenticationForm()
context = {'form':form}
return render(request,'login.html',context)
def logout_view(request):
logout(request)
return redirect('login')
#login_required
def profile(request):
Profile.objects.get_or_create(user=request.user)
if request.method == 'POST':
u_form = UserUpdateForm(request.POST,instance=request.user)
p_form = ProfileForm(request.POST,request.FILES,instance=request.user.profile)
if u_form.is_valid() and p_form.is_valid():
u_form.save()
p_form.save()
messages.success(request,'Profile updated successfully!')
return redirect('profile')
else:
u_form = UserUpdateForm(instance=request.user)
p_form = ProfileForm(instance=request.user.profile)
context = {
## 'user':request.user,
'u_form':u_form,
'p_form':p_form,
}
return render(request,'profile.html',context)
signals.py
#receiver(post_save, sender=User)
def create_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
#receiver(post_save, sender=User)
def save_profile(sender, instance, **kwargs):
instance.profile.save()
models.py
class Profile(models.Model):
name = models.OneToOneField(User,on_delete=models.CASCADE)
mobile = models.IntegerField(null=True)
address = models.CharField(max_length=350,null=True)
image = models.ImageField(upload_to='profile_pics', default='default.png',null=True)
def __str__(self):
return str(self.name.username)+'\'s Profile'
forms.py
class UserUpdateForm(forms.ModelForm):
email = forms.EmailField()
class Meta:
model = User
fields = ['username', 'email']
class ProfileForm(forms.ModelForm):
class Meta:
model = Profile
fields = ['mobile', 'address', 'image']
Error msg:
RelatedObjectDoesNotExist at /login/
User has no profile.
I am facing an issue that the user profile could not get created or updated although I used the signals for the profile model. When I login a registered user, the error message told me that User has no profile. However if I login a user that has created a profile it could show the related information so for newly registered user how to automatically create a profile for that user?
because you have a existing which does not have any profile because didn't load signal then wile creating that user if it is superuser create the new superuser from terminal delete the existing one who doesn't have profile after login in admin panel by the way i am also using signal for my web here is the code how i use it
my models.py
from django.db import models
from django.contrib.auth.models import User
from django.utils import timezone
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,)
profile_pic = models.ImageField(upload_to='profile_pics', default='default.png',)
first_name = models.CharField(max_length=50, blank=True)
last_name = models.CharField(max_length=75, blank=True)
dob = models.DateField(blank=True, null=True)
joined_date = models.DateTimeField(default=timezone.now,editable=False)
update_at = models.DateTimeField(auto_now=True)
def __str__(self):
return self.user.username
#receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, *args, **kwargs):
if created:
Profile.objects.create(user=instance)
#receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
instance.profile.save()
tell me if you still got error
I'm using two ModelForms to create na user with a Profile instance. Profile ModelForm has a single field. I'm doing form.save(commit=False), setting the required field (country) other than the one in the form and saving. I get an error saying that 'country_id' cannot be null.
models.py
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
country = models.ForeignKey(Country, on_delete=models.CASCADE)
bio = models.TextField(blank=True)
avatar = models.ImageField(upload_to='avatars/', null=True, blank=True)
email_confirmed = models.BooleanField(default=False)
#receiver(post_save, sender=User)
def update_user_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
instance.profile.save()
forms.py
class UserRegisterForm(UserCreationForm):
class Meta:
model = User
fields = ['username', 'email', 'password1', 'password2']
class ProfileRegisterForm(forms.ModelForm):
class Meta:
model = Profile
fields = ['country']
views.py
def register(request):
if request.method == 'POST':
user_form = UserRegisterForm(request.POST)
profile_form = ProfileRegisterForm(request.POST)
if user_form.is_valid() and profile_form.is_valid():
user = user_form.save(commit=False)
user.is_active = False
profile = profile_form.save(commit=False)
profile.user = user
user.save()
current_site = get_current_site(request)
subject = 'Activate Your Account'
message = render_to_string('account_activation_email.html', {
'user': user,
'domain': current_site.domain,
'uid': urlsafe_base64_encode(force_bytes(user.pk)).decode(),
'token': account_activation_token.make_token(user)
})
user.email_user(subject, message)
return redirect('activation_email_sent')
else:
user_form = UserRegisterForm()
profile_form = ProfileRegisterForm()
return render(request, 'register.html', {'user_form': user_form, 'profile_form': profile_form})
Yeah. I removed the post_save, made country nullable and saved user and profile separately. Thanks for the help.
views.py
def register(request):
if request.method == 'POST':
user_form = UserRegisterForm(request.POST)
profile_form = ProfileRegisterForm(request.POST)
if user_form.is_valid() and profile_form.is_valid():
user = user_form.save(commit=False)
user.is_active = False
user.save()
profile = profile_form.save(commit=False)
profile.user = user
profile.save()
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})
I am trying to create an "edit profile" page/form and have followed the SimpleIsBetterThanComplex tutorial on how to extend the user model by way of the OneToOne Link. However, when i try to display the user profile form to allow the user to update their profile, the form doesn't display at all except for the Submit button... Any ideas where i might have gone wrong here?
models.py
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 django.utils import timezone
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
#account types
is_userA = models.BooleanField('User A', default=True)
is_userB = models.BooleanField('User B', default=False)
is_superuser = models.BooleanField('SuperUser', default=False)
#other fields here
avatar = models.ImageField('avatar', upload_to='static/media/images/avatars/', null=True, blank=True)
phone = models.CharField('phone number', max_length=20, blank=True, default='')
address = models.CharField('address', max_length=100, default='', blank=True)
city = models.CharField('city', max_length=100, default='', blank=True)
state = models.CharField('state', max_length=2, default='', blank=True)
country = models.CharField('country', max_length=100, default='', blank=True)
date_joined = models.DateTimeField(default=timezone.now)
#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()
forms.py
class UserForm(forms.ModelForm):
class Meta:
model = User
fields = ('first_name', 'last_name', 'email')
views.py
#login_required
#transaction.atomic
def update_profile(request):
if request.method == 'POST':
user_form = UserForm(request.POST, instance=request.user)
if user_form.is_valid():
user_form.save()
messages.success(request, _('Your profile was successfully updated!'))
return redirect('settings:profile')
else:
messages.error(request, _('Please correct the error below.'))
else:
user_form = UserForm(instance=request.user)
return render(request, 'profiles/profile.html', {
'user_form': user_form,
})
template
<div class="col-md-12">
<div class="mb32">
<h5 class="uppercase">General Information</h5>
</div>
<div class="row">
<form method="post">
{% csrf_token %}
{{ user_form.as_p }}
<button type="submit">Save changes</button>
</form>
Your view looks for now looks like this:
views.py
#login_required
#transaction.atomic
def update_profile(request):
if request.method == 'POST':
user_form = UserForm(request.POST, instance=request.user)
if user_form.is_valid():
user_form.save()
messages.success(request, _('Your profile was successfully updated!'))
return redirect('settings:profile')
else:
messages.error(request, _('Please correct the error below.'))
else:
user_form = UserForm(instance=request.user)
return render(request, 'profiles/profile.html', {
'user_form': user_form,
})
According to this view if method POST, you validate enterd data from the form, but when its GET method you just define UserForm, so i think you have to make indent in the last line:
views.py
#login_required
#transaction.atomic
def update_profile(request):
if request.method == 'POST':
user_form = UserForm(request.POST, instance=request.user)
if user_form.is_valid():
user_form.save()
messages.success(request, _('Your profile was successfully updated!'))
return redirect('settings:profile')
else:
messages.error(request, _('Please correct the error below.'))
else:
user_form = UserForm(instance=request.user)
return render(request, 'profiles/profile.html', {
'user_form': user_form,
})
So your last line return render(request, 'profiles/profile.html', {'user_form': user_form,}) have to be under else statement, cause its GET as I understand.
I am very new to Django and I am trying to create a registration form. This is the relevant part of my model:
class UserProfile(models.Model):
user = models.OneToOneField(User)
university = models.ForeignKey('University')
course_title = models.ForeignKey('Course_Title')
class Meta:
db_table = 'user_profile'
def create_user_profile(sender, instance, created, **kwargs):
if created:
UserProfile.objects.create(user=instance)
post_save.connect(create_user_profile, sender=User)
This is the form I am using:
from django.contrib.auth.forms import UserCreationForm
from django import forms
from django.contrib.auth.models import User
from shelf.models import University, Course_Title, UserProfile
class RegistrationForm(UserCreationForm):
error_messages = {
'duplicate_email': "A user with that e-mail already exists.",
'email_mismatch': "The two e-mail fields didn't match.",
}
# An e-mail is being used instead of the usual Django username.
username = forms.EmailField(label="E-mail")
username2 = forms.EmailField(label="Confirm E-mail")
university = forms.ModelChoiceField(queryset=University.objects.all(), empty_label="Please choose a university")
course_title = forms.ModelChoiceField(queryset=Course_Title.objects.all(), empty_label="Please choose a course title")
class Meta:
model = User
fields = ("first_name", "last_name", "university", "course_title", "username", "username2")
.
.
.
def save(self, commit=True):
user = super(RegistrationForm, self).save(commit=False)
user.save()
user_profile = UserProfile(user=user, university=self.cleaned_data["university"], course_title=self.cleaned_data["course_title"])
if commit:
user_profile.save()
return user_profile
And, this is my view:
def index(request):
if request.method == 'POST':
form = RegistrationForm(request.POST)
if form.is_valid():
new_user = form.save()
return HttpResponseRedirect("/")
else:
form = RegistrationForm()
return render(request, "shelf/register.html", {'form': form, })
I get the error user_profile.university_id may not be NULL. I have looked a lot for why this happens and I understand why it happens. But I can't seem to fix it.
Thanks in advance.
It looks like the problem is caused by your signal handler:
def create_user_profile(sender, instance, created, **kwargs):
if created:
UserProfile.objects.create(user=instance) # no university here
post_save.connect(create_user_profile, sender=User)
Please try disable this handler and test again.
If you want the signal handler, please try changing the form.save method:
def save(self, commit=True):
user = super(RegistrationForm, self).save(commit=False) #this should trigger the signal handler to create a UserProfile instance, but not commit
user_profile = user.get_profile()
user_profile.university=self.cleaned_data["university"]
user_profile.course_title=self.cleaned_data["course_title"]
if commit:
user.save()
user_profile.save()
Be careful, if you create a user in the shell, you still get the same error, I think maybe you could add default value for university and course_title field in UserProfile model:
class UserProfile(models.Model):
user = models.OneToOneField(User)
university = models.ForeignKey('University', default=1)
course_title = models.ForeignKey('Course_Title', default=1)
I've the same issue but couldn't solve it though I followed the steps
Model
from django.db import models
from django.contrib.auth.models import User
from django.db.models.signals import post_save
class UserProfile(models.Model):
url = models.URLField()
home_address = models.TextField()
user = models.ForeignKey(User, unique=True)
def create_user_profile(sender, instance, created, **kwargs):
if created:
UserProfile.objects.create(user=instance)
post_save.connect(create_user_profile, sender=User)
View
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.models import User, Group
from django import forms
from django.shortcuts import render_to_response
from django.http import HttpResponse
from django.template.context import RequestContext
from django.views.decorators.csrf import csrf_protect
class RegisterForm(UserCreationForm):
permission_choices = (
('1', 'test1'),
('2', 'test2'),
)
email = forms.EmailField(required=False)
first_name = forms.CharField(required=False)
last_name = forms.CharField(required=False)
url = forms.URLField()
permission = forms.ChoiceField(choices=permission_choices)
class Meta(UserCreationForm.Meta):
model = User
fields = ('first_name', 'last_name', 'url', 'username', 'email', 'password1', 'password2', 'permission')
def save(self, commit=True):
user = super(RegisterForm, self).save(commit=False)
user_profile = user.get_profile()
user.email = self.cleaned_data['email']
user.first_name = self.cleaned_data['first_name']
user.last_name = self.cleaned_data['last_name']
user.permission = self.cleaned_data['permission']
user_profile.url = self.cleaned_data['url']
if commit:
user.save()
user_profile.save()
return user
#csrf_protect
def register(request):
if request.method == 'POST':
form = RegisterForm(request.POST)
if form.is_valid():
new_user = form.save()
if new_user.permission == 'q':
try:
new_user.groups.add(Group.objects.get(name='test1'))
except Group.DoesNotExist:
u = User.objects.get(pk=new_user.pk)
u.delete()
return HttpResponse('The Organisation you\'ve just choosed doesn\' exist')
return HttpResponse('OK')
else:
form = RegisterForm()
return render_to_response('registration/user.html', {'form': form}, context_instance=RequestContext(request))