Django sort form fields from two models - django

I have extended the Django user model with some extra fields
models.py
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
telephone = models.CharField(max_length=15, blank=True)
email_address = models.CharField(max_length=30, blank=True)
date_of_birth = 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()
forms.py
class ProfileForm(forms.ModelForm):
class Meta:
model = Profile
fields = ('telephone', 'email_address', 'date_of_birth')
widgets = {
'date_of_birth': forms.DateInput(attrs={'type': 'date'}),
}
views.py
def response_form(request):
if request.method == 'POST':
user_form = UserForm(request.POST, instance=request.user)
profile_form = ProfileForm(request.POST, instance=request.user.profile)
if user_form.is_valid() and profile_form.is_valid():
profile, created = Profile.objects.get_or_create(user=request.user)
user_form.save()
profile_form.save()
messages.success(request, ('Your profile was successfully updated!'))
date_of_birth = profile_form.cleaned_data['date_of_birth']
user = profile_form.cleaned_data['user']
context = {
'user': user,
'date_of_birth': date_of_birth
}
template = loader.get_template('thank_you.html')
return HttpResponse(template.render(context, request))
else:
messages.error(request, ('Please correct the error below.'))
else:
user_form = UserForm()
profile_form = ProfileForm()
return render(request, 'response_form.html', {'user_form': user_form, 'profile_form': profile_form})
When the template is loaded it places the user fields above the profile fields.
How can I place the User dropbox above the First name field?

Since you're mixing the order of the fields on two different forms, there's no way to do that whilst still using {{ my_form.as_p }} or similar methods to render your forms in your template.
You have to render your form fields individually, as explained here.
Note that when having multiple forms in one view, it's always wiser to add a prefix to your forms, in order to ensure that field names are unique in your HTML page. In your case, you might have a conflict with the email field.
Also when successfully completing the form, it's standard practice to redirect (302 REDIRECT) your user to the 'thank you' view rather than render the 'thank you' template (200 OK). This is because a page refresh (F5 or cmd-R) will otherwise resubmit the data.

Related

Django post_save() signal to register new user

Below is the createprofileview class-based view to register a new user and also to create the profile of that user at the same time.
class CreateProfileView(CreateView):
model = Profile
def post(self, request):
user_form = UserForm(request.POST)
profile_form = ProfileForm(request.POST, request.FILES)
if user_form.is_valid() and profile_form.is_valid():
user_form.save()
return HttpResponseRedirect(reverse('home', args=[]))
messages.warning(request, 'Something went wrong in Venter, please try again')
def get(self, request):
user_form = UserForm()
profile_form = ProfileForm()
return render(request, './mysite/registration.html', {'user_form': user_form, 'profile_form': profile_form})
I have used post_save() signal along with my Profile model as follows:
class Profile(models.Model):
user = models.OneToOneField(
User,
on_delete=models.CASCADE,
primary_key = True
)
organisation_name = models.ForeignKey(
Organisation,
on_delete= models.CASCADE,
null=True,
)
profile_picture = models.ImageField(
upload_to='Organisation/Employee Profile Picture/%Y/%m/%d/',
null=True,
blank=True,
)
phone_number = models.CharField(
blank=True,
max_length=10
)
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()
However, the user gets created and saved, but its profile details do not get saved.
I am unsure how to tweak the signals function suitable to my requirements of registering a new user and also creating their profile at the same time.
Here is the forms.py:
class UserForm(forms.ModelForm):
class Meta:
model = User
fields = ('username', 'password', 'email', 'first_name', 'last_name')
class ProfileForm(forms.ModelForm):
class Meta:
model = Profile
fields = ('organisation_name', 'phone_number', 'profile_picture')
I don't see the point of having a profile creation signal, because you are already having a ProfileForm to get the Profile Data. I think you can get rid of the profile signals and update the view to save the profile form directly. Like this:
if user_form.is_valid() and profile_form.is_valid():
user = user_form.save()
profile = profile_form.save(commit=False)
profile.user = user
profile.save()
# rest of the code
Update
from comments
You need to set your password in User object. You can do that in your form's save method by overriding like this(using set_password method):
class UserForm(...):
...
def save(self, **kwargs):
user = super(UserForm, self).save(commit=False)
password = self.cleaned_data.get('password')
user.set_password(password)
user.save()
return user

views.py for extended user model by onetoone to add profile information

I am extending User model to add profile information using onetoone relation. The user should be able to change the basic user profile info.
What should be the views to add such functionality?
Here is my model
Models.py
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
description = models.CharField(max_length=100, default='No description field')
city = models.CharField(max_length=100, default='Location not added')
phone = models.CharField(max_length=10, default='',blank= True)
image = models.ImageField(upload_to='products/profile/%Y%/%m/%d', blank=True)
def __str__(self):
return self.user.username
Here is my form
forms.py
class UserForm(forms.ModelForm):
class Meta():
model = UserProfile
fields = ['description', 'city', 'phone', 'image']
What should I include in my views?
views.py
#login_required()
def edit_profile(request):
pass
I want to update the model the extended UserProfile instance in a way that the previous info gets overwritten and the new information is updated.
You need to handle the UserForm in the view like any other usual form.
here what you need to do:
views.py
def edit_profile(request):
if request.method == 'POST':
# don't forget to pass request.FILES since you have imageField
form = UserForm(request.POST,
request.FILES,
instance=request.user.userprofile)
if form.is_valid():
form.save()
return redirect(reverse('to_user_profile_url'))
else:
form = UserForm(instance=request.user.userprofile)
context = {'form': form}
return render(request, 'edit_profile_template', context)

Creating a User Profile page using OneToOne field with User Model

I'm currently using Django all-auth, and it has a /accounts/profile page which I want to create/populate with a form which updates user information.
I have a Teacher field, which extends the User Model using OneToOne field.
models.py
class Teacher(models.Model):
user = models.OneToOneField(User, on_delete=models.PROTECT, related_name='Teacher')
bio = models.TextField(max_length=500, blank=True)
availability = models.BooleanField(default=False)
teacher_logo = models.FileField()
This teacher model is what I want the user to update in /accounts/profile.
forms.py
class UserForm(forms.ModelForm):
class Meta:
model = User
fields = ('first_name', 'last_name', 'email')
class TeacherForm(forms.ModelForm):
class Meta:
model = Teacher
fields = ('availability', 'bio','teacher_logo')
views.py
#login_required
#transaction.atomic
def update_profile(request):
if request.method == 'POST':
user_form = UserForm(request.POST, instance=request.user)
teacher_form = TeacherForm(request.POST, instance=request.user.teacher)
if user_form.is_valid() and teacher_form.is_valid():
user_form.save()
teacher_form.save()
messages.success(request, _('Your profile was successfully updated!'))
return redirect('users:index')
else:
messages.error(request, _('Please correct the error below.'))
else:
user_form = UserForm(instance=request.user)
teacher_form = TeacherForm(instance=request.user.teacher)
return render(request, 'accounts/profile.html', {
'user_form': user_form,
'teacher_form': teacher_form
})
template users/profile.html
<form method="post">
{% csrf_token %}
{{ user_form.as_p }}
{{ teacher_form.as_p }}
<button type="submit">Save changes</button>
</form>
urls.py
url(r'^profile/$', views.update_profile, name='Update-Profile')
I can use an update view, but then I need to specify in the URL, which seems an incorrect way of doing it; Also, users will be able to edit someone else profiles.
When I run the above, I get a complaint that 'User' object has no attribute 'teacher'.
When I remove .teacher from TeacherForm(instance=request.user.teacher) It loads the page with the form, but when I update, it still gives me the same complaint (removed in both places in views.py)
EDIT: models.py extra
#receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
Teacher.objects.create(user=instance)
#receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
instance.Teacher.save()
you set related name as Teacher, so you need:
teacher_form = TeacherForm(instance=request.user.Teacher)
# ^^^^
or better set related_name to 'teacher'
class Teacher(models.Model):
user = models.OneToOneField(
User,
on_delete=models.PROTECT,
related_name='teacher')
# ^^^

Django - User creates User and Profile in same form

Hi have changed my view from this:
How to make User able to create own account with OneToOne link to Profile - Django
For some reason the profile_form is not saving the 'user' information to the database. I have checked the information in the print(profile_form.user) data and does show the username in the terminal. It is not however saving this foreign key to the database, it just leaves it Null.
To this:
Views.py
class IndexView(View):
template_name = 'homepage.html'
form = UserCreationForm
profile_form = ProfileForm
def post(self, request):
user_form = self.form(request.POST)
profile_form = self.profile_form(request.POST)
if user_form.is_valid() and profile_form.is_valid():
user = user_form.save()
profile_form.save(commit=False)
profile_form.user = user
print(profile_form.user)
print(profile_form)
profile_form.save()
return render(request, self.template_name)
else:
return render(request, self.template_name, {'user_form': self.form,
'profile_form': self.profile_form})
def get(self, request):
if self.request.user.is_authenticated():
return render(request, self.template_name)
else:
return render(request, self.template_name, {'user_form': self.form, 'profile_form': self.profile_form})
Forms.py
class ProfileForm(ModelForm):
"""
A form used to create the profile details of a user.
"""
class Meta:
model = Profile
fields = ['organisation', 'occupation', 'location', 'bio']
Models.py
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, null=True)
organisation = models.CharField(max_length=100, blank=True)
occupation = models.CharField(max_length=100, blank=True)
bio = models.TextField(max_length=500, blank=True)
location = models.CharField(max_length=30, blank=True)
The user is an attribute of the instance, not the form.
user = user_form.save()
profile = profile_form.save(commit=False)
profile.user = user
print(profile.user)
profile.save()

RelatedObjectDoesNotExist while using custom User model in Django

I have an account app in which I have created a Profile model by extending the custom user model. I have created a view which allows the user to edit his profile info and also I have corresponding UserEditForm and ProfileEditForm. As of now, no user has a profile so when I open the edit form I get an error: "RelatedObjectDoesNotExist at /account/edit/".
" User has no profile "
I tried to create the profile using admin , then the error goes away. How can I correct this in my views.py file.
views.py
#login_required
def edit(request):
if request.method =='POST':
user_form = UserEditForm(instance=request.user,data=request.POST)
profile_form = ProfileEditForm(instance=request.user.profile,data=request.POST,files=request.FILES)
if user_form.is_valid() and profile_form.is_valid():
user_form.save()
profile_form.save()
messages.success(request,'Profile updated successfully')
else:
messages.error(request,'Error updating your profile')
else:
user_form = UserEditForm(instance=request.user)
profile_form = ProfileEditForm(instance=request.user.profile)
context = {
'user_form':user_form,
'profile_form': profile_form
}
return render(request,'account/edit.html',context)
models.py
CATEGORY_CHOICES = (
('SA','School Admin'),
('T','Teacher'),
('S','Student'),
('P','Parent'),
)
class Profile(models.Model):
eduser = models.OneToOneField(settings.AUTH_USER_MODEL)
photo = models.ImageField(upload_to='users/%Y/%m/%d',blank=True)
about_me = models.TextField(max_length=200,blank=True)
category = models.CharField(max_length=1,choices=CATEGORY_CHOICES,blank=True)
date_of_birth = models.DateField(blank=True,null=True)
def __str__(self):
return 'Profile for user {}'.format(self.eduser.username)
forms.py
class UserEditForm(forms.ModelForm):
class Meta:
model = User
fields = ('first_name','last_name','email')
class ProfileEditForm(forms.ModelForm):
class Meta:
model = Profile
fields = ('category','date_of_birth','about_me','photo')
You need to catch the error. You can do it at the top of the function:
try:
profile = request.user.profile
except ObjectDoesNotExist:
profile = Profile(user=request.user)
and pass that profile into the ProfileEditForm in both if branches.