Python Django User Registration, Login, Logout — Custom Styling

Skolo Online Learning
7 min readJan 25, 2021

User registration is a crucial part of most web applications, and Django is no different. In this article, I will cover the full project for Django User Registration, Login, Logout.

This tutorial will cover detailed user registration, including custom form input fields, custom form styling, CSS and HTML. This is necessary if you have your own custom HTML and styling and want to render a form with your specific styling, we will extend the standard Django forms to show you how to include custom styling elements.

Python Django User Registration Custom HTML and CSS

Getting started

This tutorial assumes you already have a Django project going, if not — follow this article getting started with python django web development.

Make sure you are in the project root, where the manage.py file is located and run the command to create a new app, we are going to call users:

Do not forget to register the new app in the settings.py file. Under installed apps, just add ‘users’ to the list.

python manage.py startapp users

This command will create a new folder called users, the contents of the folder must look like this below:

If a folder or file does not exists, simple create it:

users/ 

├── __init__.py
|__ admin.py
│__ apps.py
|__ forms.py
|__ migrations/
|__ models.py
|__ templates/
│ ├── register.html
│ ├── login.html
│ └── logout.html
|__ tests.py
└── views.py

Extend UserCreationForm

We need to create a form that will be used in our user registration process. There is a default Django form called UserCreationForm, that is available for this process. Only problem is the form has limited fields, plus we need to add custom styling.

The solution is to extend this form, using the User class — also a default Django class. We have already seen this User class, when we created our first superusers for the app -> https://blog.tati.digital/2021/01/25/add-user-registration-to-python-django-web-application-login-logout/

Inside the forms.py file, add the following code:

Start with the imports for the file:

from django import formsfrom django.contrib.auth.models import Userfrom django.contrib.auth.forms import UserCreationForm

Start by importing forms, then the model User and finally the default UserCreationForm — that we are going to extend.

Create a new form that extends UserCreationForm:

class RegisterForm(UserCreationForm):email = forms.EmailField(max_length=100,required = True,help_text='Enter Email Address',widget=forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'Email'}),)first_name = forms.CharField(max_length=100,required = True,help_text='Enter First Name',widget=forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'First Name'}),)last_name = forms.CharField(max_length=100,required = True,help_text='Enter Last Name',widget=forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'Last Name'}),)username = forms.CharField(max_length=200,required = True,help_text='Enter Username',widget=forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'Username'}),)password1 = forms.CharField(help_text='Enter Password',required = True,widget=forms.PasswordInput(attrs={'class': 'form-control', 'placeholder': 'Password'}),)password2 = forms.CharField(required = True,help_text='Enter Password Again',widget=forms.PasswordInput(attrs={'class': 'form-control', 'placeholder': 'Password Again'}),)check = forms.BooleanField(required = True)

We have added a new field here — check, which is not generally included in the User class. We require this form in the HTML — to track that users have accepted our terms and conditions. In fact we will make it a required field — so the form cannot be submitted if the box is not checked.

We have also added the widget attribute that allows us to style the TextInput fields by adding “class” and “placeholder” attributes — all necessary if we want to use custom styling.

And do not forget the Meta Class:

class Meta:model = Userfields = ['username', 'email', 'first_name', 'last_name', 'password1', 'password2', 'check',]

Create a view to display our form

Inside the views.py file:

Start with the imports:

from django.shortcuts import render, redirectfrom django.contrib import messagesfrom .forms import RegisterForm

We need to import RegisterForm, the new class we just created extending UserCreationForm.

Create a new function called register:

def register(request):if request.method == 'GET':form  = RegisterForm()context = {'form': form}return render(request, 'register.html', context)if request.method == 'POST':form  = RegisterForm(request.POST)if form.is_valid():form.save()user = form.cleaned_data.get('username')messages.success(request, 'Account was created for ' + user)return redirect('home_page')else:print('Form is not valid')messages.error(request, 'Error Processing Your Request')context = {'form': form}return render(request, 'register.html', context)
return
render(request, 'register.html', {})

In this register function, we are going to handle to type of requests. Since we have a form — we need to handle the POST request, over and above the usual GET request.

We are also displaying a success message to the client when a valid form has been submitted and — re-routing the user to the ‘home-page’ for now.

Front end Register HTML File

We will now display the full file, just the form part of the file.

The first variable we must have on the form is csrf_token, displayed at the top of the form.

<form class="login-form" method="POST">
{% csrf_token %}
<div class="row"><div class="col-md-12">{% if form.errors %}{% for field in form %}{% for error in field.errors %}<div class="alert alert-danger"><strong>{{ error|escape }}</strong><strong>{{ field|escape }}</strong></div>{% endfor %}{% endfor %}{% for error in form.non_field_errors %}<div class="alert alert-danger"><strong>{{ error|escape }}</strong></div>{% endfor %}{% endif %}</div><div class="col-md-6"><div class="form-group position-relative"><label>First name <span class="text-danger">*</span></label>{{form.first_name}}</div>{% if form.first_name.errors %}{% for error in form.first_name.errors %}<div class="alert alert-danger"><strong>{{ error|escape }}</strong></div>{% endfor %}{% endif %}</div><div class="col-md-6"><div class="form-group position-relative"><label>Last name <span class="text-danger">*</span></label>{{form.last_name}}</div>{% if form.last_name.errors %}{% for error in form.last_name.errors %}<div class="alert alert-danger"><strong>{{ error|escape }}</strong></div>{% endfor %}{% endif %}</div>
<div class="col-md-12">
<div class="form-group position-relative"><label>Your Email <span class="text-danger">*</span></label>{{form.email}}</div>{% if form.email.errors %}{% for error in form.email.errors %}<div class="alert alert-danger"><strong>{{ error|escape }}</strong></div>{% endfor %}{% endif %}</div><div class="col-md-12"><div class="form-group position-relative"><label>Username <span class="text-danger">*</span></label>{{form.username}}</div>{% if form.username.errors %}{% for error in form.username.errors %}<div class="alert alert-danger"><strong>{{ error|escape }}</strong></div>{% endfor %}{% endif %}</div><div class="col-md-12"><div class="form-group position-relative"><label>Password <span class="text-danger">*</span></label>{{form.password1}}</div>{% if form.password1.errors %}{% for error in form.password1.errors %}<div class="alert alert-danger"><strong>{{ error|escape }}</strong></div>{% endfor %}{% endif %}</div><div class="col-md-12"><div class="form-group position-relative"><label>Confirm Password <span class="text-danger">*</span></label>{{form.password2}}</div>{% if form.password2.errors %}{% for error in form.password2.errors %}<div class="alert alert-danger"><strong>{{ error|escape }}</strong></div>{% endfor %}{% endif %}</div>
<div class="col-md-12">
<div class="form-group"><div class="custom-control">{{form.check}}<label >I Accept <a href="{% url 'terms' %}" class="text-primary">Terms And Condition</a></label></div>{% if form.check.errors %}{% for error in form.check.errors %}<div class="alert alert-danger"><strong>{{ error|escape }}</strong></div>{% endfor %}{% endif %}</div></div><div class="col-md-12"><button class="btn btn-primary w-100" type="submit" >Register</button></div><div class="mx-auto"><p class="mb-0 mt-3"><small class="text-dark mr-2">Already have an account ?</small> <a href="{% url 'login' %}" class="text-dark font-weight-bold">Sign in</a></p></div></div></form>

There is a lot of coding in this HTML file that is not typical for a Django form, this is because we have entered every field manually. Mainly because we wanted to preserve our styling — but also to show you how. We have also styled our errors manually and chosen where to display them.

Each field will be rendered, and if the form is valid — it will be saved, if not — the html will be rendered with the form errors.

Create the URL pattern for register page

Inside the urls file, from the project directory — add the following line:

Import the view at the top of the file (the path import should already be in your urls file):

from django.urls import pathfrom users import views as users_views

Add the views inside the views list:

....
path('register/',users_views.register, name='register'),
....

Register a user

You should be able to go to yourapp/register and see your new register page, which should work.

Add Login, Logout URL paths

You can now add the path required for login.html and logout.html.

Django provides us with views, so this process is simpler. We will not be creating our own views for login/logout. We are going to be using the Django built in Class Based Views for login/logout.

Inside your urls.py file again, add the following code:

Import the views:

from django.contrib.auth import views as auth_views

Add the paths to the path — list:

path('login/', auth_views.LoginView.as_view(template_name='login.html'), name='login'),path('logout/', auth_views.LogoutView.as_view(template_name='logout.html'), name='logout'),

What we are doing here is: use a class called ‘LoginView’ from the imported auth_views. We call it as_view() and enter the location of the html file as an argument. That is all we need. Of course we need the html files . . .

Inside your templates folder, create a file called login.html

  • Only showing the form

Don’t forget the csrf_token

<form class="login-form" method="POST">{% csrf_token %}<div class="row"><div class="col-lg-12"><div class="form-group position-relative"><label>Username <span class="text-danger">*</span></label>{{form.username}}</div></div><div class="col-lg-12"><div class="form-group position-relative"><label>Password <span class="text-danger">*</span></label>{{form.password}}</div></div><div class="col-lg-12 mb-0"><button class="btn btn-primary w-100" type="submit">Login</button></div><div class="col-12 text-center"><p class="mb-0 mt-3"><small class="text-dark mr-2">Don't have an account ?</small> <a href="{% url 'register' %}" class="text-dark font-weight-bold">Sign Up</a></p></div></div></form>

For the logout.html

<div class="home-center"><div class="home-desc-center"><div class="container"><div class="row justify-content-center"><div class="col-lg-4 col-md-6"><div class="login-page bg-white shadow rounded p-4"><div class="text-center"><h4 class="mb-4">You have been logged out</h4><br><div class="row"><div class="col-md-6"><a href="{% url 'login' %}"><button class="btn btn-primary w-100">Login</button></a></div><div class="col-md-6"><a href="{% url 'home_page' %}"><button class="btn btn-info w-100">Home</button></a></div></div></div></div> <!--end col--></div><!--end row--></div> <!--end container--></div></div>

That is it . . . . .

Python Django User Registration, Login, Logout Video Tutorial

There is a lot of detail that cannot be covered in a written tutorial, for the full video tutorial — see youtube link below.

Full completed code will be made available at the end of the video series.

--

--

Skolo Online Learning

Founder of Tati Digital, passionate about digital transformation and ushering businesses in to the digital age and 4th industrial revolution.