12- Django User Registration, Profile Editing, Password Reset
In this post, we will actually implement 3 different things including User Registration, User Profile Edit and User Password Reset. We need 3 different forms for these functionalities and I will start by creating these forms.
My forms will inherit from Django Forms named UserCreationForm, UserChangeForm, PasswordChangeForm .
I actually created 2 forms while extending the user model,now I am going to modify them.
REGISTRATION:
Open forms.py and import UserCreationForm and UserChangeForm
The form I am creating inherits from UserCreationForm.
I created MyDateInput because I want to use a datepicker on my form for the birthdate field.
Also I want to use radio selection for Sex(Gender) selection, Therefore I created SexOptions.
from django import forms
from django.contrib.auth.forms import UserCreationForm, UserChangeForm
from .models import MyCustomUser
class MyDateInput(forms.DateInput):
input_type ='date'
class MyCustomUserCreationForm(UserCreationForm):
#ChoiceField Choices For User Sex
M = 'Male'
F = 'Female'
SexOptions = ((M, u'Male'),(F, u'Female'))
first_name = forms.CharField(label='', required=False, max_length=50, widget=forms.TextInput(attrs={ 'class':'form-control', 'placeholder': 'Enter Your First Name'}))
last_name = forms.CharField(label='', required=False, max_length=50, widget=forms.TextInput(attrs={ 'class':'form-control', 'placeholder': 'Enter Your Last Name'}))
dateofbirth = forms.DateField(required=False, widget=MyDateInput)
sex = forms.ChoiceField(choices=SexOptions, required=False, widget=forms.RadioSelect())
city = forms.CharField(label='', max_length=50, required=False, widget=forms.TextInput(attrs={ 'class':'form-control', 'placeholder': 'Enter Your City'}))
country = forms.CharField(label='', max_length=50, required=False, widget=forms.TextInput(attrs={ 'class':'form-control', 'placeholder': 'Enter Your Country'}))
class Meta:
model = MyCustomUser
fields = ['username', 'email', 'first_name', 'last_name', 'password1', 'password2', 'dateofbirth', 'sex', 'city', 'country']
#here, we customize the form elements of the abstracted MyCustomUserCreationForm
def __init__(self, *args, **kwargs):
super(MyCustomUserCreationForm, self).__init__(*args, **kwargs)
self.fields['username'].widget.attrs['class'] = 'form-control'
self.fields['username'].widget.attrs['placeholder'] = 'Enter a Username'
self.fields['username'].label=''
self.fields['username'].help_text='<div class="form-text text-muted"><small>Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.</small></div>'
self.fields['password1'].widget.attrs['class'] = 'form-control'
self.fields['password1'].widget.attrs['placeholder'] = 'Enter a Password'
self.fields['password1'].label=''
self.fields['password1'].help_text='<ul class="form-text text-muted small"><li>Your password can\'t be too similar to your other personal information.</li><li>Your password must contain at least 8 characters.</li><li>our password can\'t be a commonly used password.</li><li>Your password can\'t be entirely numeric.</li></ul>'
self.fields['password2'].widget.attrs['class'] = 'form-control'
self.fields['password2'].widget.attrs['placeholder'] = 'Verify the Password'
self.fields['password2'].label=''
self.fields['password2'].help_text='<div class="form-text text-muted"><small>Enter the same password as before, for verification.</small></div>'
self.fields['email'].widget.attrs['class'] = 'form-control'
self.fields['email'].widget.attrs['placeholder'] = 'Enter Your Email'
self.fields['email'].label=''
self.fields['email'].help_text=''
Now I need to create a url for registration in moviesapp urls.py
from django.urls import path
from . import views
urlpatterns = [
path('', views.moviesapp, name='moviesapp'),
path('loginuser', views.loginuser, name='loginuser'),
path('logoutuser', views.logoutuser, name='logoutuser'),
path('registeruser/', views.registeruser, name='registeruser'),
]
Then create a view in views.py for registration. Import MyCustomUserCreationForm from moviesapp.forms
from moviesapp.forms import MyCustomUserCreationForm
def registeruser(request):
if request.method == 'POST':
form = MyCustomUserCreationForm(request.POST)
if form.is_valid():
form.save()
#form.cleaned_data returns a dictionary of validated form input fields and their values as objects
user_name = form.cleaned_data['username']
pass_word = form.cleaned_data['password1']
userobj= authenticate(request, username= user_name, password=pass_word)
login(request, userobj)
messages.success(request, "Registered Successfully", extra_tags='green')
return redirect('moviesapp')
else:
form = MyCustomUserCreationForm()
context = {'form':form}
return render(request, 'registeruser.html', context)
Now we need to create a template (registeruser.html) in crmapp's template folder
{% extends 'base.html' %}
{% block title%}
<title>Registeration Page</title>
{% endblock title%}
{% block content %}
<div class="row justify-content-center">
<div class="col-4">
<div class="card">
<div class="card-body">
<h2 class="text-center">Register</h2>
<div class="col-md-6 offset-md-3">
<form method="POST" action="{% url 'registeruser' %}">
{% csrf_token %}
{% if form.errors %}
<div class="alert alert-danger alert-dismissible fade show" role="alert">
<strong>Your Form has errors</strong>
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
</div>
{% endif %}
{{form.as_p}}
<input type="submit" value="Register" class="btn btn-secondary mb-2">
</form>
</div>
</div>
</div>
</div>
{% endblock content %}
In our Navbar we have a button for registration already, we just need to give a link to registeruser.
base.html
<a class="btn btn-outline-light" href="/{% url 'registeruser' %}">Register</a>
Restart Apache service. This is how it looks:
create a few accounts and check if there are any problems.
USER PROFILE EDIT:
Let's create our form first to edit user profiles.
Open forms.py and import UserChangeForm.
from django import forms
from django.contrib.auth.forms import UserCreationForm, UserChangeForm
from .models import MyCustomUser
class MyCustomUserChangeForm(UserChangeForm):
email = forms.EmailField(max_length=100, widget=forms.TextInput(attrs={'class':'form-control'}))
first_name = forms.CharField(max_length=50, widget=forms.TextInput(attrs={'class':'form-control'}))
last_name = forms.CharField(max_length=50, widget=forms.TextInput(attrs={'class':'form-control'}))
city = forms.CharField(max_length=50, required=False, widget=forms.TextInput(attrs={'class':'form-control'}))
country = forms.CharField(max_length=50, required=False, widget=forms.TextInput(attrs={'class':'form-control'}))
class Meta:
model = MyCustomUser
fields = ['username', 'first_name', 'last_name', 'email', 'city', 'country']
def __init__(self, *args, **kwargs):
super(MyCustomUserChangeForm, self).__init__(*args, **kwargs)
self.fields['username'].widget.attrs['class'] = 'form-control'
self.fields['username'].help_text='<div class="form-text text-muted"><small>Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.</small></div>'
Create a url in moviesapp urls.py.
path('editprofile/', views.editprofile, name='editprofile'),
In base.html. Give a link for editprofile on the NavBar.
{% if user.is_authenticated %}
<ul class="navbar-nav" >
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle text-white" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
{{user.username}}'s Profile
</a>
<ul class="dropdown-menu" style="background-color: #86aac4;">
<li><a class="dropdown-item text-white" href="#">My List</a></li>
<li><a class="dropdown-item text-white" href="/{% url 'editprofile' %}">My Account</a></li>
<li><hr class="dropdown-divider"></li>
<li><a class="dropdown-item text-white" href="/{% url 'logoutuser' %}">Log Out</a></li>
</ul>
</li>
<li class="nav-item">
<a class="nav-link disabled">Disabled</a>
</li>
</ul>
We need to create a view. Import MyCustomUserChangeForm from moviesapp and create a view named editprofile in views.py
from moviesapp.forms import MyCustomUserChangeForm, MyCustomUserCreationForm
def editprofile(request):
if request.method == 'POST':
form = MyCustomUserChangeForm(request.POST, instance=request.user)
if form.is_valid():
form.save()
messages.success(request, "Your profile is updated", extra_tags='green')
return redirect('editprofile')
else:
#User data will be fetched when user access his/her profile
form = MyCustomUserChangeForm(instance=request.user)
context = {'form':form}
return render(request, 'editprofile.html', context)
And Finally create a template (editprofile.html) for it.
{% extends 'base.html' %}
{% block title%}
<title>Edit User Profile</title>
{% endblock title%}
{% block content %}
<h2 class="text-center">Edit User Profile</h2>
<div class="col-md-6 offset-md-3">
<form method="POST" action="{% url 'editprofile' %}">
{% csrf_token %}
{% if form.errors %}
<div class="alert alert-danger alert-dismissible fade show" role="alert">
<strong>Your Form has errors</strong>
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
{% endif %}
{{form.as_p}}
<input type="submit" value="Save" class="btn btn-secondary mb-2">
</form>
</div>
{% endblock content %}
Restart Apache service.
PASSWORD RESET:
As you can see above, the Edit Profile Form has a build-in link to reset password but the link it provides is not the url I defined. So, I will I hide this message and link and create my own link. To do that I will just modify MyCustomChangeForm and add password field as a hidden field.
class MyCustomUserChangeForm(UserChangeForm):
password = forms.CharField(required=False, label="", widget=forms.TextInput(attrs={'type':'hidden'}))
email = forms.EmailField(max_length=100, widget=forms.TextInput(attrs={'class':'form-control'}))
first_name = forms.CharField(max_length=50, widget=forms.TextInput(attrs={'class':'form-control'}))
last_name = forms.CharField(max_length=50, widget=forms.TextInput(attrs={'class':'form-control'}))
city = forms.CharField(max_length=50, required=False, widget=forms.TextInput(attrs={'class':'form-control'}))
country = forms.CharField(max_length=50, required=False, widget=forms.TextInput(attrs={'class':'form-control'}))
class Meta:
model = MyCustomUser
fields = ['username', 'first_name', 'last_name', 'email', 'city', 'country']
def __init__(self, *args, **kwargs):
super(MyCustomUserChangeForm, self).__init__(*args, **kwargs)
self.fields['username'].widget.attrs['class'] = 'form-control'
self.fields['username'].help_text='<div class="form-text text-muted"><small>Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.</small></div>'
Now I am adding my own link to editprofile.html
{% extends 'base.html' %}
{% block title%}
<title>Edit User Profile</title>
{% endblock title%}
{% block content %}
<h2 class="text-center">Edit User Profile</h2>
<div class="col-md-6 offset-md-3">
<form method="POST" action="{% url 'editprofile' %}">
{% csrf_token %}
{% if form.errors %}
<div class="alert alert-danger alert-dismissible fade show" role="alert">
<strong>Your Form has errors</strong>
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
{% endif %}
{{form.as_p}}
<input type="submit" value="Save" class="btn btn-secondary mb-2">
</form>
<div>
<a href="/{% url 'changepassword' %}">Change Password</a>
</div>
</div>
Open forms.py and import PasswordChangeForm and create a new form from PasswordChangeForm
from django import forms
from django.contrib.auth.forms import UserCreationForm, UserChangeForm, PasswordChangeForm
from .models import MyCustomUser
class MyCustomUserPasswordChangeForm(PasswordChangeForm):
def __init__(self, *args, **kwargs):
super(PasswordChangeForm, self).__init__(*args, **kwargs)
self.fields['old_password'].widget.attrs['class'] = 'form-control'
self.fields['new_password1'].widget.attrs['class'] = 'form-control'
self.fields['new_password2'].widget.attrs['class'] = 'form-control'
In views.py, import MyCustomPasswordChangeForm
from moviesapp.forms import MyCustomUserChangeForm, MyCustomUserCreationForm, MyCustomUserPasswordChangeForm
Create a view in views.py
def changepassword(request):
if request.method == 'POST':
form = MyCustomUserPasswordChangeForm(data=request.POST, user=request.user)
if form.is_valid():
form.save()
messages.success(request, "Your password is updated", extra_tags='green')
return redirect('changepassword')
else:
form = MyCustomUserPasswordChangeForm(user=request.user)
context = {'form':form}
return render(request, 'changepassword.html', context)
Create a url in urls.py
path('changepassword/', views.changepassword, name='changepassword'),
Create a template and name it changepassword.html in moviesapp's template folder
{% extends 'base.html' %}
{% block title%}
<title>Change Password</title>
{% endblock title%}
{% block content %}
<h2 class="text-center">Change Password</h2>
<div class="col-md-6 offset-md-3">
<form method="POST" action="{% url 'changepassword' %}">
{% csrf_token %}
{% if form.errors %}
<div class="alert alert-danger alert-dismissible fade show" role="alert">
<strong>Your Form has errors</strong>
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
{% endif %}
{{form.as_p}}
<input type="submit" value="Save" class="btn btn-secondary mb-2">
</form>
</div>
{% endblock content %}
Restart Apache. Our password change form looks like this.
After changing the password, it logs off the user by default. You might want to keep this behavior or you may want to keep the user logged on. I want my users stay logged on after they change the password. To do that, I will import, update_session_auth_hash in views.py first.
from django.contrib.auth import authenticate, login, logout, update_session_auth_hash
Then modify the changepassword view like this
def changepassword(request):
if request.method == 'POST':
form = MyCustomUserPasswordChangeForm(data=request.POST, user=request.user)
if form.is_valid():
form.save()
update_session_auth_hash(request, form.user)
messages.success(request, "Your password is updated", extra_tags='green')
return redirect('changepassword')
else:
form = MyCustomUserPasswordChangeForm(user=request.user)
context = {'form':form}
return render(request, 'changepassword.html', context)
Restart Apache service
That's all.
- Hits: 293