My dear reader, how are you? السلام عليكم
Keep love in your heart. A life without it is like a sunless garden when the flowers are dead – Oscar Wilde
This is the forth part of DjangoBlog tutorial series. This post extends our application with features like user login, logout, registration, change password and etc.
Few useful links to practically follow the project:
- DjangoBlog GitHub repository — DirectMe
- All other tutorials on Django-Blog — DirectMe
- Set your GitHub repo to DjangoBlog Part 3 using the following command:
git fetch origin 042f28b475dabd8d4e4dd52b3eb04067a773407f
We will use the Django authentication framework for this tutorial for managing user access on DjangoBlog. We will start by creating a new Django application called account
(blogenv)Django-Blog-Application$ django-admin startapp account # open config/settings.py and update with new app INSTALLED_APPS = [ 'account', 'taggit', ... ... ] (blogenv)Django-Blog-Application$ python3 manage.py migrate
A default Django project comes with an authentication framework with auth application, default views, and middleware classes, i.e., AuthenticationMiddleware and SessionMiddleWare. It also includes 3 built-in models, that are, 1) User (user model with username, password, email, first_name, last_name and is_active field), 2) Group (a model to put users into categories) and 3) Permissions (flags for users or groups to perform certain actions).
CREATING a User Login View
We will follow the steps as shown below to achieve adding a user log-in feature
1) Create a log-in form
# create account/forms.py and add the following program into it from django import forms class LoginForm(forms.Form): username = forms.CharField() password = forms.CharField(widget=forms.PasswordInput)
2) Then add a login view as shown below
# open account/views.py and add the following program into it from django.shortcuts import render from django.http import HttpResponse from django.contrib.auth import authenticate, login from .forms import LoginForm def user_login(request): if request.method == 'POST': form = LoginForm(request.POST) if form.is_valid(): cd = form.cleaned_data user = authenticate(request, username=cd['username'], password=cd['password']) if user is not None: if user.is_active: login(request, user) return HttpResponse('Authenticated successfully') else: return HttpResponse('Disabled account') else: return HttpResponse('Invalid login') else: form = LoginForm() return render(request, 'account/login.html', {'form': form})
3) We then need to add the routes.
# create account/urls.py and add the following from django.urls import path from . import views urlpatterns = [ path('login/', views.user_login, name='login'), ] # open config/urls.py and add the path to accounts app from django.conf.urls import path, include from django.contrib import admin import blog.urls account.urls urlpatterns = [ path('admin/', admin.site.urls), path('blog/', include('blog.urls', namespace='blog')), path('account/', include('account.urls')), ]
4) Finally, add the templates for user log-in
# create new directories and template as shown below (blogenv)Django-Blog-Application$ mkdir account/templates (blogenv)Django-Blog-Application$ mkdir account/templates/account (blogenv)Django-Blog-Application$ touch account/templates/account/login.html # open account/templates/account/login.html and add the following code into it {% extends "base.html" %} {% block title %}Log-in{% endblock %} {% block content %} <h1>Log-in</h1> <p>Please, use the following form to log-in:</p> <form action="." method="post"> {{ form.as_p }} {% csrf_token %} <p><input type="submit" value="Log in"></p> </form> {% endblock %}
5) Now, you should be able to see the log-in page in your browser by starting the server at http://127.0.0.1:8000/account/login/
Using Django AUTH APP Default Login and logout View
1) First, add routes to the default views
# open account/urls.py and add the following from django.urls import path from . import views from django.contrib.auth import views as auth_views urlpatterns = [ #path('login/', views.user_login, name='login'), path('login/', auth_views.LoginView.as_view(), name='login'), path('logout/', auth_views.LogoutView.as_view(), name='logout'), ]
2) Add a template for login and logout that uses the default login/logout views
# create new directories and template as shown below (blogenv)Django-Blog-Application$ mkdir account/templates/registration (blogenv)Django-Blog-Application$ touch account/templates/registration/login.html (blogenv)Django-Blog-Application$ touch account/templates/registration/logged_out.html # open account/templates/registration/login.html and add the following program into it {% extends "base.html" %} {% block title %}Log-in{% endblock %} {% block content %} <h1>Log-in</h1> {% if form.errors %} <p> Your username and password didn't match. Please try again. </p> {% else %} <p>Please, use the following form to log-in. If you don't have an account <a href="{% url "register" %}">register here</a></p> {% endif %} <div class="login-form"> <form action="{% url 'login' %}" method="post"> {{ form.as_p }} {% csrf_token %} <input type="hidden" name="next" value="{{ next }}" /> <p><input type="submit" value="Log-in"></p> </form> </div> {% endblock %} # open account/templates/registration/logged_out.html and add the following program into it {% extends "base.html" %} {% block title %}Logged out{% endblock %} {% block content %} <h1>Logged out</h1> <p>You have been successfully logged out. You can <a href="{% url "login" %}">log-in again</a>.</p> {% endblock %}
3) Create a redirect when the user login and add user login/log-out status and link to the header base template of the application
# open config/settings.py and add the following program # on successful redirect user should be broght back to blog page LOGIN_REDIRECT_URL = '/blog' LOGIN_URL = 'login' LOGOUT_URL = 'logout' # open templates/base.html and add the following div in header tag <div> <span class="user"> {% if request.user.is_authenticated %} Hello {{ request.user.first_name }}, <a href="{% url "logout" %}">Logout</a> {% else %} <a href="{% url "login" %}">Log-in</a> {% endif %} </span> <div>
Changing password views
We will do it in two steps
1) Add the route to default password change and password change done view as shown below
# open account/urls.py and add the following program in bold urlpatterns = [ #path('login/', views.user_login, name='login'), path('login/', auth_views.LoginView.as_view(), name='login'), path('logout/', auth_views.LogoutView.as_view(), name='logout'), path('password_change/', auth_views.PasswordChangeView.as_view(), name='password_change'), path('password_change/done/', auth_views.PasswordChangeDoneView.as_view(), name='password_change_done'), ]
2) Add the templates for both views as shown below
# create two templates as shown below (blogenv)Django-Blog-Application$ touch account/templates/registration/password_change_form.html (blogenv)Django-Blog-Application$ touch account/templates/registration/password_change_done.html # open password_change_form.html and add the following program {% extends "base.html" %} {% block title %}Change you password{% endblock %} {% block content %} <h1>Change you password</h1> <p>Use the form below to change your password.</p> <form action="." method="post"> {{ form.as_p }} <p><input type="submit" value="Change"></p> {% csrf_token %} </form> {% endblock %} # open password_change_done.html and add the following program {% extends "base.html" %} {% block title %}Password changed{% endblock %} {% block content %} <h1>Password changed</h1> <p>Your password has been successfully changed.</p> {% endblock %}
Resetting password views
We will again follow a list of similar steps as above described below
1) add routes to default password reset views as shown below
# Open account/urls.py and add the following path('password_reset/', auth_views.PasswordResetView.as_view(), name='password_reset'), path('password_reset/done/', auth_views.PasswordResetDoneView.as_view(), name='password_reset_done'), path('reset/<uidb64>/<token>/', auth_views.PasswordResetConfirmView.as_view(), name='password_reset_confirm'), path('reset/done/', auth_views.PasswordResetCompleteView.as_view(), name='password_reset_complete'),
2) Add the templates for all the views for password change as shown below
# create four templates as shown below (blogenv)Django-Blog-Application$ touch account/templates/registration/password_reset_form.html (blogenv)Django-Blog-Application$ touch account/templates/registration/password_reset_email.html (blogenv)Django-Blog-Application$ touch account/templates/registration/password_reset_done.html (blogenv)Django-Blog-Application$ touch account/templates/registration/password_reset_confirm.html (blogenv)Django-Blog-Application$ touch account/templates/registration/password_reset_complete.html # open password_reset_form.html and add the following program {% extends "base.html" %} {% block title %}Reset your password{% endblock %} {% block content %} <h1>Forgotten your password?</h1> <p>Enter your e-mail address to obtain a new password.</p> <form action="." method="post"> {{ form.as_p }} <p><input type="submit" value="Send e-mail"></p> {% csrf_token %} </form> {% endblock %} # open password_reset_email.html and add the following program Someone asked for password reset for email {{ email }}. Follow the link below: {{ protocol }}://{{ domain }}{% url "password_reset_confirm" uidb64=uid token=token %} Your username, in case you've forgotten: {{ user.get_username }} # open password_reset_done.html and add the following program {% extends "base.html" %} {% block title %}Reset your password{% endblock %} {% block content %} <h1>Reset your password</h1> <p>We've emailed you instructions for setting your password.</p> <p>If you don't receive an email, please make sure you've entered the address you registered with.</p> {% endblock %} # open password_reset_confirm.html and add the following program {% extends "base.html" %} {% block title %}Reset your password{% endblock %} {% block content %} <h1>Reset your password</h1> {% if validlink %} <p>Please enter your new password twice:</p> <form action="." method="post"> {{ form.as_p }} {% csrf_token %} <p><input type="submit" value="Change my password" /></p> </form> {% else %} <p>The password reset link was invalid, possibly because it has already been used. Please request a new password reset.</p> {% endif %} {% endblock %} # open password_reset_complete.html and add the following program {% extends "base.html" %} {% block title %}Password reset{% endblock %} {% block content %} <h1>Password set</h1> <p>Your password has been set. You can <a href="{% url "login" %}">log in now</a></p> {% endblock %} # finally, open login.html and add the link to password reset # write the the end before the closing of div <p><a href="{% url "password_reset" %}">Forgotten your password?</a></p>
Registering a new user
Follow the following steps to add a user registration feature
1) We will first add a user registration form
# open account/forms.py and add the following class UserRegistrationForm(forms.ModelForm): password = forms.CharField(label='Password', widget=forms.PasswordInput) password2 = forms.CharField(label='Repeat password', widget=forms.PasswordInput) class Meta: model = User fields = ('username', 'first_name', 'email') def clean_password2(self): cd = self.cleaned_data if cd['password'] != cd['password2']: raise forms.ValidationError('Passwords don\'t match.') return cd['password2']
2) create a registration view and add routes
# open account/views.py and add the following program def register(request): if request.method == 'POST': user_form = UserRegistrationForm(request.POST) if user_form.is_valid(): new_user = user_form.save(commit=False) new_user.set_password( user_form.cleaned_data['password']) new_user.save() return render(request, 'account/register_done.html', {'new_user': new_user}) else: user_form = UserRegistrationForm() return render(request, 'account/register.html', {'user_form': user_form}) # open account/urls.py and add path('register/', views.register, name='register'),
3) add templates for registeration as shown below
(blogenv)Django-Blog-Application$ touch account/templates/account/register.html (blogenv)Django-Blog-Application$ touch account/templates/account/register_done.html # open register.html and add the following program {% extends "base.html" %} {% block title %}Create an account{% endblock %} {% block content %} <h1>Create an account</h1> <p>Please, sign up using the following form:</p> <form action="." method="post"> {{ user_form.as_p }} {% csrf_token %} <p><input type="submit" value="Create my account"></p> </form> {% endblock %} # open register_done.html and add the following program {% extends "base.html" %} {% block title %}Welcome{% endblock %} {% block content %} <h1>Welcome {{ new_user.first_name }}!</h1> <p>Your account has been successfully created. Now you can <a href="{% url "login" %}">log in</a>.</p> {% endblock %} # open registration/login.html and add the following program to add link for registration <p>Please, use the following form to log-in. If you don't have an account <a href="{% url "register" %}">register here</a></p>
3) Now, you should be able to see the log-in page in your browser by starting the server at http://127.0.0.1:8000/account/register/
I will add two more functionalities in this application. One is to extend the user model to have profile pictures and the other is to use messaging framework in my future posts. Furthermore, we will create a user profile as well.
If you run the development server at this point in time you should be able to see the added functionalities at http://127.0.0.1:8000/blog/ or pull the code from GitHub and test it on your local machine
I hope you find this tutorial useful. If you find any errors or feel any need for improvement, let me know in your comments below.
Signing off for today. Stay tuned and I will see you next week! Happy learning.