In this Python tutorial, we will learn “how to register user with OTP verification in Django”. we will build a project ‘otpverification’ that takes user information and send the OTP then verifies the OTP entered by the user to register the user in the database.
To create the project follow the below steps:
Open a command prompt and navigate to the directory where you want to create your Django Project.
mkdir django_otpverify
Change the directory to django_otpverify and create a virtual environment.
python -m venv env
Activate the virtual environment ‘env’ using the below code.
env\Scripts\activate
Install the Django framework.
pip install django
Create a project named ‘otpverification’ using the below code.
django-admin startproject otpverification
Create an app named ‘account’ using the below code.
python manage.py startapp account
Run the below command to open the project in the visual studio.
code .
Now open the file ‘setting.py’ in the project folder named ‘otpverification` and the created app ‘account’ in the INSTALLED_APPS section as shown in the below picture.
INSTALLED_APPS = [
. . . .,
'account'
]
Then run the below code into the terminal to create a database and its table that already exist in the Django project.
python manage.py migrate
After creating the ‘account’ app, open the file ‘account/models.py’ and define the new model ‘JobUser’.
from django.db import models
from django.contrib.auth.models import AbstractBaseUser
from .managers import UserManager
from phonenumber_field.modelfields import PhoneNumberField
class JobUser(AbstractBaseUser):
VERIFICATION_TYPE = [
('sms','SMS'),
]
phone_number = PhoneNumberField(unique = True)
verification_method = models.CharField(max_length=10,choices= VERIFICATION_TYPE)
is_active = models.BooleanField(default= True)
is_admin = models.BooleanField(default= False)
is_staff = models.BooleanField(default= False)
is_superuser = models.BooleanField(default=False)
USERNAME_FIELD = "phone_number"
objects = UserManager()
def __str__(self):
return str(self.phone_number)
def has_perm(self, perm, obj=None):
return self.is_admin
def has_module_perms(self, app_label):
return self.is_admin
Register the model JobUser in the file named ‘account\admin.py’ using the below code.
from django.contrib import admin
from .models import JobUser
admin.site.register(JobUser)
After writing the above code, you need to install the package ‘pip install django-phonenumber-field’ and ‘pip install django-phonenumbers’ so run the below code in your terminal.
pip install django-phonenumber-field
pip install django-phonenumbers
Then create a manager named ‘UserManager’ for the JobUser by creating the new file at ‘account/managers.py’.
from django.contrib.auth.models import BaseUserManager
from django.db import models
class UserManager(BaseUserManager):
def create_user(self, phone_number, password = None, verification_method = 'sms',**extra_fields):
if not phone_number:
raise ValueError("The Phone Number field is required")
user = self.model(phone_number = phone_number, verification_method = verification_method,**extra_fields)
user.set_password(password)
user.save(using = self._db)
return user
def create_superuser(self, phone_number, password = None, **extra_fields):
extra_fields.setdefault('is_staff', True)
extra_fields.setdefault('is_superuser', True)
user = self.create_user(phone_number,password, verification_method='sms',**extra_fields)
user.is_admin = True
user.save(using=self._db)
return user
Run the below command to create the table ‘JobUser’ in the database.
python manage.py makemigrations account
python manage.py migrate
Then create two forms named ‘UserRegistrationForm’ and ‘LoginForm’ in the new file named ‘account/forms.py’.
from django import forms
from .models import JobUser
class UserRegistrationForm(forms.ModelForm):
password = forms.CharField(widget=forms.PasswordInput)
verification_method = forms.ChoiceField(
choices=JobUser.VERIFICATION_TYPE,
widget=forms.RadioSelect
)
class Meta:
model = JobUser
fields = ('phone_number', 'password', 'verification_method')
class UserLoginForm(forms.Form):
phone_number = forms.CharField()
password = forms.CharField(widget=forms.PasswordInput)
Create a templates folder in the account app, within the templates folder make three new HTML files named ‘base.html’, ‘registration.html’, and ‘login.html’.
‘base.html’ file
<!DOCTYPE html>
<html>
<head>
<title>{% block title %}User Phone OTP Verification{% endblock %}</title>
<!-- Include necessary CSS files and stylesheets -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
<style>
/* Add custom styles here */
</style>
</head>
<body>
<!-- Navigation bar -->
<nav class="navbar navbar-expand navbar-dark bg-dark">
<a class="navbar-brand" href="#">User Phone OTP Verification</a>
<ul class="navbar-nav ml-auto">
<li class="nav-item">
<a class="nav-link" href="{% url 'user_registration' %}">Register</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{% url 'user_login' %}">Login</a>
</li>
</ul>
</nav>
<!-- Page content -->
<div class="container mt-4">
{% block content %}
<!-- Content of each individual page will be inserted here -->
{% endblock %}
</div>
<!-- Include necessary JS files and scripts -->
<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.5.3/dist/umd/popper.min.js"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>
<script>
// Add custom scripts here
</script>
</body>
</html>
Create a ‘registration.html’ to get the user information.
{% extends 'base.html' %}
{% block content %}
<h2>User Registration</h2>
{% if messages %}
{% for message in messages %}
<div class="alert alert-danger">{{ message }}</div>
{% endfor %}
{% endif %}
<form method="post" action="{% url 'user_registration' %}">
{% csrf_token %}
{{ form.as_p }}
{% if otp_required %}
<div class="form-group">
<label for="id_otp">OTP:</label>
<input type="text" id="id_otp" name="otp" class="form-control" required>
</div>
<button type="submit" class="btn btn-primary">Verify OTP</button>
{% else %}
<button type="submit" class="btn btn-primary">Generate OTP</button>
{% endif %}
</form>
{% endblock %}
Create a ‘login.html’ to get the user login information.
{% extends 'base.html' %}
{% block content %}
<h2>User Login</h2>
{% if messages %}
{% for message in messages %}
<div class="alert alert-danger">{{ message }}</div>
{% endfor %}
{% endif %}
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit" class="btn btn-primary">Login</button>
</form>
{% endblock %}
For sending and receiving the sms on the phone, here we will use Twilio. So install the Twilio package in your project using the below code by running it in your terminal.
pip install twilio
After installing the Twilio package, create a view for the user registration, login, and OTP verification by following the below steps:
Open the file ‘views.py’ at the location ‘account/views.py’, and import the required libraries or modules.
from django.shortcuts import render
from django.shortcuts import render, redirect
from django.views import View
from .forms import UserRegistrationForm, UserLoginForm
from django.contrib import messages
import random
from twilio.rest import Client
from otpverification import settings
from django.contrib.auth import get_user_model
Create the view named ‘OTPVerificationView’ using the below code.
class OTPVerificationView(View):
def post(self, request):
submitted_otp = request.POST.get('otp')
saved_otp = request.session.get('otp')
password = request.session.get('password')
if submitted_otp == saved_otp:
messages.success(request, "OTP verification successful")
phone_number = request.session.get('phone_number')
User = get_user_model()
user = User.objects.create(phone_number=phone_number)
user.set_password(password)
user.save()
return redirect('user_login')
else:
messages.error(request, "Invalid OTP. Please try again.")
return redirect('otp_verification')
Create the view named ‘UserRegistrationView’ using the below code.
class UserRegistrationView(View):
def get(self, request):
form = UserRegistrationForm()
return render(request, 'registration.html', {'form': form, 'otp_required': False})
def post(self, request):
form = UserRegistrationForm(request.POST)
if form.is_valid():
phone_number = str(form.cleaned_data['phone_number'])
verification_method = form.cleaned_data['verification_method']
if 'otp' in request.POST:
return OTPVerificationView.as_view()(request)
else:
password = form.cleaned_data['password']
otp = generate_otp()
if verification_method == 'sms':
send_sms_otp(phone_number, otp)
request.session['otp'] = otp
request.session['phone_number'] = phone_number
request.session['password'] = password
form.fields['password'].widget.attrs['value'] = password
return render(request, 'registration.html', {'form': form, 'otp_required': True, 'password': password})
return render(request, 'registration.html', {'form': form, 'otp_required': False})
Create a view named ‘UserLoginView’ using the below code.
class UserLoginView(View):
def get(self, request):
form = UserLoginForm()
return render(request, 'login.html', {'form': form})
def post(self, request):
form = UserLoginForm(request.POST)
if form.is_valid():
messages.success(request,'Login SuccessFul')
mobile_number = form.cleaned_data['phone_number']
password = form.cleaned_data['password']
otp = form.cleaned_data['otp']
return render(request, 'login.html',{'form':form})
Also, create the two function named ‘generate_otp()’ and ‘send_sms_otp()’ that is used in the ‘UserRegistrationView’ for generating random OTP and sending SMS on the user’s phone respectively. Create these two functions at the bottom of the view.py file
def generate_otp():
return str(random.randint(100000, 999999))
def send_sms_otp(phone_number, otp):
client = Client(settings.TWILIO_ACCOUNT_SID, settings.TWILIO_AUTH_TOKEN)
message = client.messages.create(
body=f'Your OTP is: {otp}',
from_=settings.TWILIO_PHONE_NUMBER,
to=phone_number
)
Open the file ‘setting.py’ and add the following lines to set up your JobUser Model and Twilio sms service as shown in the below picture.
AUTH_USER_MODEL = 'account.JobUser'
TWILIO_ACCOUNT_SID = 'Enter the ACCOUNT_SID from the Twilio '
TWILIO_AUTH_TOKEN = 'Enter the AUTH_TOKEN from the Twilio'
TWILIO_PHONE_NUMBER = 'Specify the Phone Number from Twilio '
You can get the above information such as ACCOUNT_SID, AUTH_TOKEN, and PHONE_NUMBER from the Twilio console.
Open the file ‘urls.py’ of the project ‘otpverification’ and add the following URLs.
from django.contrib import admin
from django.urls import include, path
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('account.urls'))
]
Now open the file ‘urls.py’ of the app level ‘account’ and add the following URLs.
from django.contrib import admin
from django.urls import path
from .views import UserRegistrationView, UserLoginView, OTPVerificationView
urlpatterns = [
path('register/', UserRegistrationView.as_view(), name='user_registration'),
path('login/', UserLoginView.as_view(), name ='user_login'),
path('verify-otp/', OTPVerificationView.as_view(), name='otp_verification'),
]
Now run the server or your project using the below command in the terminal.
python manage.py runserver
If you see something like shown in the above picture, then your project is working correctly. To test your project enter the URL in the browser ‘http://127.0.0.1:8000/register/’ and you will see the registration page for the user as shown in the below picture.
Enter the required details like Phone number, and password and choose the verification method as SMS, then click on the button Generate OTP. After this, you receive the OTP on your phone.
Now enter the received OTP and click on the button Verify OTP.
After verifying the OTP, you will be redirected to the login page where you see the message on the top ‘OTP verification successful’, if you enter the correct OTP otherwise you will see the error message ‘Invalid OTP. Please try again’.
Download the Complete OTP Verification Application
Conclusion
In this Python tutorial, we have learned how to register the user with OTP verification in Django, we learned how to use the Twilio sms service in our project and the logic to send, receive and verify the OTP.
You may also like to read
I am Bijay Kumar, a Microsoft MVP in SharePoint. Apart from SharePoint, I started working on Python, Machine learning, and artificial intelligence for the last 5 years. During this time I got expertise in various Python libraries also like Tkinter, Pandas, NumPy, Turtle, Django, Matplotlib, Tensorflow, Scipy, Scikit-Learn, etc… for various clients in the United States, Canada, the United Kingdom, Australia, New Zealand, etc. Check out my profile.