adding extra field ('city') to UserRegisterForm Django - django

When new users register I want to store:
fields = ['username', 'email', 'password1', 'password2', 'city']
so I extended UserRegisterForm by adding 'city' to the form.
It renders fine in the template and save everything except 'city'. There is no even column 'city' in the new users profile when checking by admin page so looks like its not creating one.
I found few similar posts and been following Doc but that didint help.
Have tried many different ways but will post two I think mostly sensible ones.
EXAMPLE 1
- *forms.py*
...
from django import forms
from django.contrib.auth.models import User
from django.contrib.auth.forms import UserCreationForm
class UserRegisterForm(UserCreationForm):
email = forms.EmailField()
city = forms.CharField(required=True)
class Meta:
model = User
fields = ['username', 'email', 'password1', 'password2', 'city']
def save(self, commit=True):
user = super(UserRegisterForm, self).save(commit=False)
user.city = self.cleaned_data['city']
if commit:
user.save()
return user
- *views.py*
...
from django.contrib.auth.forms import UserCreationFormfrom
from .forms import UserRegisterForm
def register(request):
if request.method == 'POST':
form = UserRegisterForm(request.POST)
if form.is_valid():
form.save()
print('VALID')
username = form.cleaned_data.get('username')
messages.success(request,
'{} Your account has been created! You can now Log In'.format(username))
return redirect('/login/')
else:
form = UserRegisterForm()
context = {
'form': form,
}
return render(request, 'users/register.html', context)
#login_required
def profile(request):
return render(request, 'users/profile.html')
- *template*
...
<form method="POST">
{% csrf_token %}
{{ form|crispy }}
<!-- {{ form2 }} -->
<button class="btn-signup" type="submit">Sign Up</button>
</form>
In Example 2 Iam creating new class 'ProfileForm' with new model as separate form and including it in the views.py in one function with UserRegisterForm.
EXAMPLE 2
- *models.py*
...
class Profile(models.Model):
city = models.CharField(max_length=25, blank=False)
def __str__(self):
return self.city
- *forms.py*
...
class UserRegisterForm(UserCreationForm):
email = forms.EmailField()
city = forms.CharField(required=True)
class Meta:
model = User
fields = ['username', 'email', 'password1', 'password2', 'city']
class ProfileForm(forms.ModelForm):
class Meta:
model = Profile
fields = ['city']
def save(self, commit=True):
user = super(UserRegisterForm, self).save(commit=False)
user.city = self.cleaned_data['city']
if commit:
user.save()
return user
- *views.py*
...
from django.contrib.auth.forms import UserCreationFormfrom
from .forms import UserRegisterForm, ProfileForm
def register(request):
if request.method == 'POST':
form = UserRegisterForm(request.POST)
form2 = ProfileForm(request.POST)
if form.is_valid() and form2.is_valid():
form.save()
form2.save()
print('VALID')
username = form.cleaned_data.get('username')
messages.success(request,
'{} Your account has been created! You can now Log In'.format(username))
return redirect('/login/')
else:
form = UserRegisterForm()
form2 = ProfileForm()
context = {
'form': form,
'form2': form2
}
return render(request, 'users/register.html', context)
- *template*
...
<form method="POST">
{% csrf_token %}
{{ form|crispy }}
{{ form2 }}
<button class="btn-signup" type="submit">Sign Up</button>
</form>

Your Profile model should have a OneToOne relation with the User model like this:
Class Profile (models.Model):
user = models.OneToOneField (User,on_delete=models.CASCADE)
city = models.CharField (max_length=25,blank=False)
You don't need to define ProfileForm.You can create profile objects for the user like this.
form = UserRegisterForm (request.POST)
if form.is_valid ():
city = form.cleaned_data ['city']
user = form.save ()
Profile.objects.create (user=user,city=city)
return redirect ('some_view)

Related

How do I make a User Profile form using OnetoOneField extending the User Model?

I would like to make a form that extends the User Model using OnetoOneField. It would basically be a form in which a user can add/update their information after they have registered.
models.py
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
username = models.CharField(max_length=120)
name = models.CharField(max_length=120) # max_length = required
email = models.EmailField(max_length=120)
paypal_id = models.CharField(max_length=120)
bio = models.TextField(max_length=500, blank=True)
def __str__(self):
return self.user.username
forms.py
class UserProfileForm(forms.ModelForm):
class Meta:
model = Profile
fields = ["username", "name", "email", "paypal_id", "bio"]
views.py
def userprofile_view(request):
if request.method == 'POST':
profile_form = UserProfileForm(request.POST)
if profile_form.is_valid():
profile = profile_form.save(commit=False)
profile.save()
return redirect('account')
else:
profile_form = UserProfileForm()
context = {'profile_form': profile_form}
return render(request, 'accounts/account_create.html', context)
template.html
{% extends 'base.html' %}
{% block content %}
<form action="." method="POST">
{% csrf_token %}
{{ profile_form.as_p }}
<input type="submit" value="Save"/>
</form>
{% endblock %}
I keep getting this error when I hit Save:
(1048, "Column 'user_id' cannot be null")
Is there any fix for this?
You can create both user and profile models at once using a generic create view.
Forms:
class UserProfileForm(forms.ModelForm):
class Meta:
model = Profile
fields = ["username", "name", "email", "paypal_id", "bio", "user"]
class UserForm(UserCreationForm):
email = forms.EmailField(required=True)
class Meta:
model = User
fields = ['username', 'first_name', 'last_name', 'email', 'password1', 'password2']
View:
class CreateUserProfileView(LoginRequiredMixin, SuccessMessageMixin, CreateView):
model = Profile
form_class = UserProfileForm
user_form_class = UserForm
template_name = 'accounts/account_create.html'
success_message = "Profile created successfully"
success_url = reverse_lazy('profile-list')
def get(self, request):
profile_form = self.form_class()
user_form = self.user_form_class()
return render(request, self.template_name, {'profile_form': profile_form, 'user_form': user_form})
def post(self, request, *args, **kwargs):
profile_form = self.form_class(request.POST)
user_form = self.user_form_class(request.POST)
if all([profile_form.is_valid(), user_form.is_valid()]):
user = user_form.save()
profile = profile_form.save(commit=False)
profile.user = user
profile.save()
messages.success(request, self.success_message)
else:
return render(request, self.template_name, {'profile_form': profile_form, 'user_form': user_form})
return HttpResponseRedirect(self.success_url)
You need to overwrite save() method of Profile to create/update User on-the-fly while saving Profile.
I.e.:
class Profile(models.Model):
...
def save(self, *args, **kwargs):
if not self.user.pk:
self.user = User.objects.create_user(self.user.username, password=self.user.password)
self.user.save() # mandatory as create_user is not recognized as save operation
super().save(*args, **kwargs)

How do I update a CustomUser value via UpdateView using a hidden logic

I've spent several hours on this and I'm not able to see any signs as to why the change on the flag is not getting through.
Please note the change form already works for all exposed fields, i.e. the user can go in and change the name or country already and it will get saved after clicking on update profile.
What I'm now trying to do is to also change the confirmed_email flag to True (but without telling or letting the user see it) whenever the client makes an update to the form.
To do this I check if the user was logged in using Linkedin (or any Social account for that matter) via something along the lines of ""if user.social_auth.exists()"". That said, it's not that i can't fulfill this function, it's that even when i use a silly condition that i know it's true the field "email_confirmed" will still NOT change to True in the back-end.
Thanks so much in advance. I appreciate your time.
PS. I'm a noob with Django but loving it.
Models.py
class CustomUser(AbstractUser):
id = models.BigAutoField(primary_key=True)
email = models.EmailField(unique=True)
email_confirmed = models.BooleanField(default=False)
country = models.CharField(max_length=30,choices=COUNTRY, null=True, blank=False)
first_name = models.CharField(max_length=50, null=False, blank=False, default="")
last_name = models.CharField(max_length=50, null=False, blank=False, default="")
Views.py
class SignUpView(CreateView):
form_class = CustomUserCreationForm
success_url = reverse_lazy('home')
template_name = 'signup.html'
...
class UpdateProfileView(UpdateView):
form_class = CustomUserChangeForm
success_url = reverse_lazy('home')
template_name = 'update_profile.html'
def get_object(self, queryset=None):
return self.request.user
Forms.py
from django import forms
from django.contrib.auth.forms import UserCreationForm, UserChangeForm
from .models import CustomUser
class CustomUserCreationForm(UserCreationForm):
class Meta(UserCreationForm.Meta):
model = CustomUser
fields = ('first_name', 'last_name','country',)
class CustomUserChangeForm(UserChangeForm):
password = None
class Meta:
model = CustomUser
fields = ('first_name', 'last_name','country',)
update_profile.html
{% extends 'base.html' %}
{% load crispy_forms_tags %}
{% block title %}Home{% endblock title %}
{% block content %}
{% if user.is_authenticated %}
<h2>Update Profile</h2>
<form method="post">
{% csrf_token %}
{{ form|crispy }}
<button class="btn btn-success" type="submit">Update</button>
</form>
{% else %}
<p>You are not logged in</p>
Log In |
Sign Up
{% endif %}
{% endblock content %}
My main attempt was to adding another leg in the view (see def ChangeActiveStatus below).
class UpdateProfileView(UpdateView):
form_class = CustomUserChangeForm
success_url = reverse_lazy('home')
template_name = 'update_profile.html'
def get_object(self, queryset=None):
return self.request.user
def ChangeActiveStatus(request):
if request.method == "POST":
form = self.form_class(request.POST)
user = form.save(commit=False)
if form.is_valid() and user.social_auth.exists() == True:
user.email_confirmed = True
form.save()
else:
form = form()
return render(request, 'login', {'form':form})
The issue here is that you're setting the email_confirmed = True on user and not form.instance. You could also save the user instance rather than calling form.save().
form = self.form_class()
if request.method == "POST":
form = self.form_class(request.POST)
if form.is_valid() and user.social_auth.exists():
user = form.save(commit=False)
user.email_confirmed = True
user.save()
return render(request, 'login', {'form':form})
Or
form = self.form_class()
if request.method == "POST":
form = self.form_class(request.POST)
if form.is_valid() and user.social_auth.exists():
form.instance.email_confirmed = True
form.save()
return render(request, 'login', {'form':form})
I ended up implementing a solution via models.py instead, basically bypassing the need to save any changes on one of the fields through the views, i.e. this is the logic, which will be triggered every time the client changes something in their profile.
def save(self, *args, **kwargs):
try:
CustomUser.objects.latest('id').id
except:
...
...
if self.is_active == True and self.email_verified == False:
self.email_verified = True
super(CustomUser, self).save(*args, **kwargs)
def __str__(self):
return self.email

Django Forms: Cannot get to data to save from ModelForm

I am creating a job board site. Right now I can successfully register an Employer but when I try to create a job listing while logged in as an Employer, the data from the form does not save to the database. I have the following models.py:
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.http import HttpResponse
# Create your models here.
class Employer(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
def __str__(self):
return self.user.first_name
#receiver(post_save, sender=User)
def create_employer(sender, instance, created, **kwargs):
if created:
Employer.objects.create(user=instance)
#receiver(post_save, sender=User)
def save_employer(sender, instance, **kwargs):
instance.employer.save()
class Job(models.Model):
poster = models.ForeignKey(Employer, on_delete=models.CASCADE)
job_title = models.CharField(max_length=50)
establishment_name = models.CharField(max_length = 50)
details = models.TextField(max_length = 2000)
salary = models.CharField(max_length = 20)
address = models.CharField(max_length = 50)
state = models.CharField(max_length = 20)
zip_code = models.CharField(max_length = 10)
def __str__(self):
return self.job_title + " - " + self.establishment_name \
+ ", " + self.poster.user.first_name + " " +self.poster.user.last_name
A user can register as an employer just fine, but I am having problems getting Jobs to save to the database. Once a user registers/logs in as an employer they are redirected to employer_home.html, where an employer can post a job:
{% extends 'core/base.html' %}
{% block body %}
<h1>Post a Job</h1>
<form>
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Post</button>
</form>
{% endblock %}
Here is my forms.py:
from django.forms import ModelForm
from .models import Job
from django.contrib.auth.models import User
from django.contrib.auth.forms import UserCreationForm
class EmployerSignupForm(UserCreationForm):
class Meta:
model = User
fields = ('first_name',
'last_name',
'email',
'username',
'password1',
'password2',)
class JobPostForm(ModelForm):
class Meta:
model = Job
fields= ('job_title',
'establishment_name',
'salary',
'address',
'state',
'zip_code',
)
and here is my employer_view(view to handle Job form):
def employer_home(request):
if request.method == 'POST':
form = JobPostForm(request.POST)
if form.is_valid():
form.save()
return HttpResponse('Working!')
else:
form = JobPostForm()
return render(request, 'core/employer_home.html', {'form': form})
employer_home.html displays a form with no problem, but when the form is submitted none of the data is saved to the database and the return HttpResponse('Working!') is never executed, it simply reloads the empty form. Does anyone know how to fix this?
Add method="POST" in your form. In your view do this:
def employer_home(request):
if request.method == 'POST':
form = JobPostForm(request.POST)
if form.is_valid():
job_object = form.save(commit=False)
job_object.poster = poster_object
job_object.save()
return HttpResponse('Working!')
else:
form = JobPostForm()
return render(request, 'core/employer_home.html', {'form': form})
A good example is shown here: example
Try <form method="post"> in your html template.
By default, the method is get.

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 - Template displays edited user context info despite ValidationError

Hi I'm working on a form to edit a user's profile. It properly updates the fields upon successful submission without any validation errors. The problem I'm having is that if the form does return a validation error upon submission and I've changed the name on the form, the template will display this new name instead of the old name. I'm assuming this is because it uses the user context provided by the POST request. Is there any way to properly display the user's old name until the form is actually successfully submitted? Below is my code.
forms.py
from django import forms
from django.contrib.auth.models import User
from django.contrib.auth.forms import UserCreationForm
from django.forms import ModelForm
class UserProfileForm(UserCreationForm):
first_name = forms.CharField(label="First Name", required=True)
last_name = forms.CharField(label="Last Name", required=True)
email = forms.EmailField(label="Email", required=True)
class Meta:
model = User
fields = ("first_name", "last_name", "email", "username", "password1", "password2")
def clean_email(self):
email = self.cleaned_data.get('email')
if email:
if User.objects.filter(email=email).exists():
raise forms.ValidationError('This email is already in use.')
return email
def save(self, commit=True):
user = super(UserProfileForm, self).save(commit=False)
user.email = self.cleaned_data["email"]
if commit:
user.save()
return user
class EditProfileForm(ModelForm):
first_name = forms.CharField(label="First Name", required=True)
last_name = forms.CharField(label="Last Name", required=True)
email = forms.EmailField(label="Email", required=True)
password1 = forms.CharField(label="New Password", widget=forms.PasswordInput)
password2 = forms.CharField(label="Repeat New Password", widget=forms.PasswordInput)
class Meta:
model = User
fields = ("first_name", "last_name", "email", "username", "password1", "password2")
def clean_email(self):
email = self.cleaned_data.get('email')
if email:
if User.objects.filter(email=email).exists():
raise forms.ValidationError('This email is already in use.')
return email
def save(self, commit=True):
user = super(EditProfileForm, self).save(commit=False)
user.email = self.cleaned_data['email']
if commit:
user.save()
return user
views.py
def register(request):
if request.method == 'POST':
form = UserProfileForm(request.POST)
if form.is_valid():
form.save()
new_user = authenticate(username=form.cleaned_data['username'],
password=form.cleaned_data['password1'])
login(request, new_user)
return redirect('/overview')
else:
if request.user.is_authenticated():
return redirect('/overview')
else:
form = UserProfileForm()
return render(request, "/register.html", {'form': form})
#login_required
def edit_profile(request):
if request.method == 'POST':
print request.POST
form = EditProfileForm(request.POST, instance=request.user)
if form.is_valid():
form.save()
return redirect('/profile')
else:
form = EditProfileForm()
return render(request, '/edit_profile.html', {'form': form})
navbar.html
<li class="">
<a href="javascript:;" class="user-profile dropdown-toggle" data-toggle="dropdown" aria-expanded="false">
<img src="/ui/assets/images/user.png" alt="">{{ user.first_name }} {{ user.last_name }}
<span class=" fa fa-angle-down"></span>
</a>
<ul class="dropdown-menu dropdown-usermenu pull-right">
<li> Profile</li>
<li><i class="fa fa-sign-out pull-right"></i> Log Out</li>
</ul>
</li>
Fetch a separate instance of the user from the db to use with the form. This way, the request.user will not be modified when the form is validated.
user = User.objects.get(id=request.user.id)
form = EditProfileForm(request.POST, instance=user)