How to Create a Quiz Web Application with Python Django

Python course with 57 real-time projects - Learn Python

FREE Online Courses: Click for Success, Learn for Free - Start Now!

Quiz – best way to check our knowledge on a particular topic.

We have visited a lot of quiz platforms lately, right! Ever wondered if you could develop one.

In this article, we will guide you to develop a quiz application using Python Django. Let’s see what all functionalities we will be developing in this project (for two roles – user and admin):

User:

  • Sign up (register)
  • Login
  • Quiz page (with timer)
  • Result page (with score, time taken, percentage score, total questions, correct answers, wrong answers)

Admin:

  • Login
  • Sign up
  • Add Question
  • Quiz Page
  • Result Page

Project Prerequisites

You should have a basic knowledge of the following technologies:

  • HTML: to display content on web page
  • CSS: to style HTML document
  • JavaScript: to make a timer
  • Bootstrap: A front-end framework
  • Python: Programming language.
  • Django: Python framework

To install Django:

pip install Django

Download Quiz App Python Code

Please download the source code of quiz web application: Quiz Web Application Python Code

Steps to Build Quiz App Project

1. Starting project:

Commands to start the project and app:

django-admin startproject DjangoQuiz
cd DjangoQuiz
django-admin startapp Quiz

2. Writing Models

Code:

from django.db import models
 
# Create your models here.
class QuesModel(models.Model):
    question = models.CharField(max_length=200,null=True)
    op1 = models.CharField(max_length=200,null=True)
    op2 = models.CharField(max_length=200,null=True)
    op3 = models.CharField(max_length=200,null=True)
    op4 = models.CharField(max_length=200,null=True)
    ans = models.CharField(max_length=200,null=True)
    
    def __str__(self):
        return self.question

We just need one model for this project, QuesModel.

Attributes of QuesModels:

  • question: This stores the question, we have also defined maximum length of the question i.e. 200
  • op1: As the quiz contains Multiple choice questions. This stores option 1 value.
  • op2: option2
  • op3: option3
  • op4: option4
  • ans: This indicates which among the four options is the correct ans.

The __str__() method returns a string representation of any object of QuesModel.

In Django when we use Sqlite3 database, we don’t have to write table definitions we just have to write models and after that, we have to run the following commands

Py manage.py makemigrations
Py manage.py migrate

3. forms.py

Code:

from django.forms import ModelForm
from .models import *
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.models import User
 
class createuserform(UserCreationForm):
    class Meta:
        model=User
        fields=['username','password'] 
 
class addQuestionform(ModelForm):
    class Meta:
        model=QuesModel
        fields="__all__"

To access, update, create or delete entries in the database (to implement CRUD functionality), we need forms.

Form makes it easy to implement CRUD functionality as we neither have to create forms to accept information from users nor we have to validate information manually, we can just use is_valid() method to validate the information before updating it in the database.

4. admin.py

Code:

from django.contrib import admin
from .models import *
 
# Register your models here.
admin.site.register(QuesModel)

Here, we are registering our model to the admin site, so that we can update or access the database from the admin panel also. But we need a superuser to access the admin site.

To create a staff (admin) user, run the below command

py manage.py createsuperuser

5. settings.py

To use javascript we have to specify the path of static files. Therefore, add the following code in settings.py

STATIC_URL = '/static/'
STATICFILES_DIRS=[BASE_DIR/'static']

6. urls.py

In this file we are just defining urls. To define urls:

  • import view functions: from Quiz.views import *
  • add url to urlpatterns: path(‘login/’, loginPage,name=’login’),

Code:

"""DjangoQuiz URL Configuration
 
The `urlpatterns` list routes URLs to views. For more information please see:
    https://docs.djangoproject.com/en/3.1/topics/http/urls/
Examples:
Function views
    1. Add an import:  from my_app import views
    2. Add a URL to urlpatterns:  path('', views.home, name='home')
Class-based views
    1. Add an import:  from other_app.views import Home
    2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home')
Including another URLconf
    1. Import the include() function: from django.urls import include, path
    2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path
from Quiz.views import *
from django.conf import settings
from django.conf.urls.static import static
 
urlpatterns = [
    path('admin/', admin.site.urls),
    path('', home,name='home'),
    path('addQuestion/', addQuestion,name='addQuestion'),
    path('login/', loginPage,name='login'),
    path('logout/', logoutPage,name='logout'),
    path('register/', registerPage,name='register'),
 
]
urlpatterns += static(settings.MEDIA_URL,document_root=settings.MEDIA_ROOT)

7. views.py :

Code:

from django.shortcuts import redirect,render
from django.contrib.auth import login,logout,authenticate
from .forms import *
from .models import *
from django.http import HttpResponse
 
# Create your views here.
def home(request):
    if request.method == 'POST':
        print(request.POST)
        questions=QuesModel.objects.all()
        score=0
        wrong=0
        correct=0
        total=0
        for q in questions:
            total+=1
            print(request.POST.get(q.question))
            print(q.ans)
            print()
            if q.ans ==  request.POST.get(q.question):
                score+=10
                correct+=1
            else:
                wrong+=1
        percent = score/(total*10) *100
        context = {
            'score':score,
            'time': request.POST.get('timer'),
            'correct':correct,
            'wrong':wrong,
            'percent':percent,
            'total':total
        }
        return render(request,'Quiz/result.html',context)
    else:
        questions=QuesModel.objects.all()
        context = {
            'questions':questions
        }
        return render(request,'Quiz/home.html',context)
 
def addQuestion(request):    
    if request.user.is_staff:
        form=addQuestionform()
        if(request.method=='POST'):
            form=addQuestionform(request.POST)
            if(form.is_valid()):
                form.save()
                return redirect('/')
        context={'form':form}
        return render(request,'Quiz/addQuestion.html',context)
    else: 
        return redirect('home') 
 
def registerPage(request):
    if request.user.is_authenticated:
        return redirect('home') 
    else: 
        form = createuserform()
        if request.method=='POST':
            form = createuserform(request.POST)
            if form.is_valid() :
                user=form.save()
                return redirect('login')
        context={
            'form':form,
        }
        return render(request,'Quiz/register.html',context)
 
def loginPage(request):
    if request.user.is_authenticated:
        return redirect('home')
    else:
       if request.method=="POST":
        username=request.POST.get('username')
        password=request.POST.get('password')
        user=authenticate(request,username=username,password=password)
        if user is not None:
            login(request,user)
            return redirect('/')
       context={}
       return render(request,'Quiz/login.html',context)
 
def logoutPage(request):
    logout(request)
    return redirect('/')

View.py is the file which contains the main or we can say the business logic of the project as this file can only access the database and also it can pass the information to templates so that it can be displayed on the web browser.

Home

This is the most important view of our project as this method is responsible for displaying the quiz page as well as the result page.

For Quiz Page: It renders all the questions from QuesModel and then it just passes all the questions to home.html

For result page: It calculates the score by giving ten marks for every correct question and zero (i.e. no negative marking) for every incorrect question. And then it calculates the percentage score.

Finally, it sends all these calculated values along with time elapsed, number of correct answers, number of incorrect answers to result.html. Result.html displays all this information.

Add Question:

This method is only for admin, if any user tries to access this method, the user is redirected to the home page. We are basically using the add question form in this to create a form object.

To validate the information entered by the user we are using is_valid method and after validating the information we are adding another question in the database using save() method.

Result.html

{% extends 'Quiz/dependencies.html' %}
 
{% block content %}
 
<div class="container ">
        
    <div class="card-columns" style="padding: 10px; margin: 20px;">
        <div class="card" align="centre " style="width: 32rem; border:5px black solid">
            <img class="card-img-top" src="http://kmit.in/emagazine/wp-content/uploads/2018/02/karnataka-results.jpg" alt="Card image cap">
            <div class="card-body">
                <h5 class="card-title">Score: {{score}}</h5>
 
                <p class="card-text">Percentage: {{percent}}%</p>
                <p class="card-text">Time Taken: {{time}} seconds</p>
                <p class="card-text">Correct answers: {{correct}}</p>
                <p class="card-text">Incorrect answers: {{wrong}}</p>
                <p class="card-text">Total questions: {{total}}</p>
                <h5>All the best for next quiz!</h5>
            </div>
        </div>
    </div>
 
</div>
 
{% endblock %}

Result page:

quiz result

Home.html

{% extends 'Quiz/dependencies.html' %}
 
{% block content %}
{% load static %}
<div class="container ">
<h1>Welcome to DataFlair Quiz</h1>
 
<div align="right " id="displaytimer"><b>Timer: 0 seconds</b></div>
 
 <form method='post' action=''>
    {% csrf_token %}
    {% for q  in questions%}
    <div class="form-group">
      <label for="question">{{q.question}}</label>
    </div>
    <div class="form-check">
        <div class="form-check">
            <input class="form-check-input" type="radio" name="{{q.question}}" id="gridRadios1" value="option1" checked>
            <label class="form-check-label" for="gridRadios1">
                {{q.op1}}
            </label>
        </div>
        <div class="form-check">
            <input class="form-check-input" type="radio" name="{{q.question}}" id="gridRadios2" value="option2">
            <label class="form-check-label" for="gridRadios2">
                {{q.op2}}
            </label>
        </div>
        <div class="form-check">
            <input class="form-check-input" type="radio" name="{{q.question}}" id="gridRadios1" value="option3">
            <label class="form-check-label" for="gridRadios1">
                {{q.op3}}
            </label>
        </div>
        <div class="form-check">
            <input class="form-check-input" type="radio" name="{{q.question}}" id="gridRadios2" value="option4">
            <label class="form-check-label" for="gridRadios2">
                {{q.op4}}
            </label>
        </div>
        <br>
    </div>    
    {% endfor %}
    <input id='timer' type='hidden' name="timer" value="">
    <br>
    <button type="submit" class="btn btn-primary">Submit</button>
  </form>
    {% block script %}
        <script>
 
            console.log('hello world')
            const timer=document.getElementById('displaytimer')
            console.log(timer.textContent)
            const inputtag = document.getElementById('timer')
 
            t=0
            setInterval(()=>{
                t+=1
                timer.innerHTML ="<b>Timer: " +t+" seconds</b>"
                inputtag.value = t
            },1000)
        </script>
    {% endblock script %}
 
</div>
{% endblock %}

Quiz page

quiz user home

For admin, only the navigation bar is different, it has one button for adding questions.

quiz admin home

Navbar.html

{% load static %}
 
<style>
  .greet{
    font-size: 18px;
    color: #fff;
    margin-right: 20px;
  }
</style>
 
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
  <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
    <span class="navbar-toggler-icon"></span>
  </button>
  <div class="collapse navbar-collapse" id="navbarNav">
 
    <ul class="navbar-nav">
      
      <li class="nav-item active">
        <a class="nav-link" href="{% url 'home' %}">Home</a>
      </li>
      </li>
      <li class="nav-item active">
        
      {% if request.user.is_staff %}
      </li>
      <li class="nav-item active">
        <a class="nav-link" href="{% url 'addQuestion' %}">Add Question</a>
      </li>
      {% endif %} 
 
      </li>
      <li class="nav-item">
        <a class="nav-link" href="{% url 'login' %}">Login</a>
      </li>
    </ul>
  </div>
 
  <span class="greet">Hello, {{request.user}}</span>
  <span ><a  class="greet" href="{% url 'logout' %}">Logout</a></span>
 
</nav>

AddQuestion.html

{% extends 'Quiz/dependencies.html' %}
 
{% block content %}
 
<div class="jumbotron container row">
    <div class="col-md-6">
        <h1>Add Question</h1>
        <div class="card card-body">
           <form action="" method="POST">
              {% csrf_token %}
                {{form.as_p}}
                <br>
             <input type="submit" name="Submit">
             </form>
            </div>
        </div>
    </div>
 
</div>
 
{% endblock %}
 

Add Question page

add quiz question

dependencies.html

{% load static %}
<html>
    <head>
        <title>
            DataFlair Online Quiz with Django
        </title>
        <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css" integrity="sha384-9aIt2nRpC12Uk9gS9baDl411NQApFmC26EwAOH8WgZl5MYYxFfc+NcPb1dKGj7Sk" crossorigin="anonymous">
    </head>
    <body>        
        {% include 'Quiz/navbar.html' %}
        {% block content %}   
        {% endblock %}
        <br>
       
        <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.min.js" integrity="sha384-OgVRvuATP1z7JjHLkuOU7Xw704+h835Lr+6QL9UvYjZE3Ipu6Tp75j7Bh/kR0JKI" crossorigin="anonymous"></script>
    </body>
</html>

Login.html

{% extends 'Quiz/dependencies.html' %}
{% load static%}
{% block content %}
<div class="container jumbotron">
    <form method="POST" action="">
        {% csrf_token %}
        <p><input type="text" name="username" placeholder="Username..."></p>
        <p><input type="password" name="password" placeholder="Password..." ></p>
        <input class="btn btn-success" type="submit" value="Login">
        <p>Do not have an account<a href='{% url 'register' %}'>Register</a></p>
    </form>
</div>
{% endblock %}

Register.html

{% extends 'Quiz/dependencies.html' %}
{% load static %}
{% block content %}
<div class="container jumbotron">
    <form method="POST" action="" >
        {% csrf_token %}
        {{form.as_p}}
       
        <input class="btn btn-success" type="submit" value="Register Account">
    </form>
</div>
{% endblock %}

Summary

We have developed an Online Quiz application Project using Python Django. We developed quiz web application with following functionalities: Login, Sign up, Add Question, Quiz Page, Result Page. You can develop more interesting projects with Django, so keep exploring and keep learning.

If you are Happy with DataFlair, do not forget to make us happy with your positive feedback on Google

follow dataflair on YouTube

27 Responses

  1. Karthika C says:

    What is username and password

  2. Ferdinand says:

    It doesn’t work. When the correct answer is chosen, it chooses it as wrong. Nothing is ever correct, always wrong and I am using exactly the code here….

    Now I am thinking, is it because of the part that said:

    if q.ans == request.POST.get(q.question):
    …….

    Because q.ans may never be equal to q.question which is the question asked. Now I changed it to :

    if q.ans == q.op1 or q.op2 or q.op3 or q.op4:
    ……….

    Everything was showing as correct answer on the result, whether i chose the right answer or not….What am i getting wrong? Help, i am new to django

  3. Athul Mohan says:

    How can I login as admin

  4. Bee Cool says:

    Hey Ferdinand,
    This is what I did. I downloaded the zip file, unzip the file (I moved the zip file on Documents and extract it there). Then I went to the directory where manage.py is located via terminal, and ran “python manage.py runserver”.
    Everything works fine.

  5. Kevin says:

    while creating the question give as optionn (i.e): option1 in the answer field. Everthing works fine otherwise

  6. Kevin says:

    While creating the question give as optionn (i.e): option1 in the answer field. Everthing works fine otherwise.

  7. sahil says:

    where i can download zip file

  8. sahil says:

    where i can download zip file

  9. VINAY KARNA says:

    That’s right. It never captures the user’s answer, it only captures the option number but never the value inside it. So, to compare actual answer we do not have the real value from the user. If anyone has found out how to get the value please comment.

  10. VINAY KARNA says:

    Use this and it should work.
    # Create your views here.
    def home(request):
    if request.method == ‘POST’:
    questions=QuesModel.objects.all()
    score=0
    wrong=0
    correct=0
    total=0
    for q in questions:

    total+=1
    answer = request.POST.get(q.question) # Gets user’s choice, i.e the key of answer
    items = vars(q) # Holds the value for choice
    print(items[answer])
    # Compares actual answer with user’s choice
    if q.ans == items[answer]:
    score+=10
    correct+=1
    else:
    wrong+=1
    percent = score/(total*10) *100
    context = {
    ‘score’:score,
    ‘time’: request.POST.get(‘timer’),
    ‘correct’:correct,
    ‘wrong’:wrong,
    ‘percent’:percent,
    ‘total’:total
    }
    return render(request,’mcqapp/result.html’,context)
    else:
    questions=QuesModel.objects.all()
    context = {
    ‘questions’:questions
    }
    return render(request,’mcqapp/home.html’,context)

  11. VINAY KARNA says:

    def home(request):
    if request.method == ‘POST’:
    questions=QuesModel.objects.all()
    score=0
    wrong=0
    correct=0
    total=0
    for q in questions:

    total+=1
    answer = request.POST.get(q.question) # Gets user’s choice, i.e the key of answer
    items = vars(q) # Holds the value for choice
    print(items[answer])
    # Compares actual answer with user’s choice
    if q.ans == items[answer]:
    score+=10
    correct+=1
    else:
    wrong+=1
    percent = score/(total*10) *100
    context = {
    ‘score’:score,
    ‘time’: request.POST.get(‘timer’),
    ‘correct’:correct,
    ‘wrong’:wrong,
    ‘percent’:percent,
    ‘total’:total
    }
    return render(request,’mcqapp/result.html’,context)
    else:
    questions=QuesModel.objects.all()
    context = {
    ‘questions’:questions
    }
    return render(request,’mcqapp/home.html’,context)

  12. heeem says:

    how to go to admin? /admin page where super user can login?

  13. O says:

    To avoid issues with the answer just enter option+number eg option1 instead of the the answer in the ans box when adding the question.

  14. Steve Farmer says:

    The problem is that the answers are hard coded in the home.html file:
    value=”option1″
    This needs to be:
    value=”{{q.op1}}”

  15. Farmer says:

    How to delete a question

  16. shivank says:

    Any way to upload Questions that has images in them as well. For Example:

    String + img.png / jpg / tiff + string or:

    String
    \n mpg.png / jpg / tiff

  17. Kirti says:

    Can some one help ,How to run it .

  18. sankar says:

    this is very great coding , but even when the right answers are choosen it is displaying wrong , kindly help with that soon pls

    thank you

  19. Ankit says:

    it shows many errors after executing it

  20. vaishnavi says:

    there is error regarding static files you should provide js,css files code also

Leave a Reply

Your email address will not be published. Required fields are marked *