Building a Quiz App with Django and Fauna
Tags: fauna, python, beginners
Published: 2021-03-09 19:19:30

In this article, we are going to explore building a quiz application using the Python Django web framework and Fauna. If this is your first time hearing about Fauna, please visit my previous article [here](https://dev.to/chukslord1/building-an-appointment-scheduler-app-with-django-and-fauna-2n9o) for a brief introduction.

# Creating The Quiz Database

The first step we are going to take is to create a new database in Fauna. To create a Fauna database, you have to first sign up [for an account](https://dashboard.fauna.com/accounts/register?utm_source%3DDevTo%26utm_medium%3Dreferral%26utm_campaign%3DWritewithFauna_ApptScheduler_Ochuku). if you have not done that already. After signing up, you can now create a new database by clicking on the `CREATE DATABASE` button on the dashboard.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/981kq6mafhlse10rjwlj.png)

After clicking on the button as shown in the image above, you then need to give your database a name and save it. For this tutorial, we are going to name our database `Quiz`. 

# Creating The Collections

You will also need to create four collections (one for `Users`,`Quiz`,`Question` and `Answers`). A collection could be thought of as a table in a database system. Click on `CREATE COLLECTION` then fill in the required fields. You will need to enter the name for the collection, the `History Days` and `TTL`. The `History Days` is the number of days you want Fauna to retain a historical record of any data in that collection while the `TTL` is an expiry date for data in the collection. For example, if the `TTL` is set to 5, any data stored in the collection will automatically be deleted 5 days after its last modified date. 

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3itzilykgo9v006ke1jl.png)

# Creating the Fauna Indexes

We will need to create eight (8) Fauna indexes that will allow us to scroll through data added to our database. The indexes are the; `user`, `quiz_index`, `quiz_get_index`, `question_index`, `question_get_index`,`question_answer`,`answer_score_index` and the `answer_get_index`. The `user` index will have a term for “username” and will be a unique field that will allow querying of the `User` collection. The `quiz_index` index will have a term for “name” and will be a unique field that will allow querying and creating new documents in the `Quiz` collection. The `quiz_get_index` index will have a term for “status” which will also allow querying of the `Quiz` collection. The `question_index` index will have a term for “question_asked” which will allow querying of the `Question` collection. The `question_get_index` will have a term for "name" which will allow querying and matching with of the data in the `Question` collection . The `question_answer` index will have a term for “correct_answer” which will also allow querying and matching of `Question` collection. The `answer_score_index` will have a term for “user” and “quiz” which will allow matching and querying of the `Answers` collection. The `answer_get_index` will have a term for “user” and “question” which will also allow matching and querying of the `Answers` collection.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yvmtlel4ghfwif6ieb41.png)

# Creating a Fauna API Key

Before you can begin building a Django app that uses Fauna, you need to create an API key that will allow our Django app to easily interact with the database. To create an API key, go to the security tab on the left side of your screen.

Click `New Key` to generate a key. You will then be required to provide a database to connect the key to, the role of the key, and an optional name for your API key. After providing the information required, click the `SAVE` button. 

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4cc5hncjnq6m7rgov8hv.png)

After saving your key, your secret key will be presented as in the image above (hidden here for privacy). Copy your secret key from the dashboard and save it somewhere you can easily retrieve it for later use.

# Designing Our Quiz App UI

## Prerequisites

From this point onwards, to follow this step by step guide on building a quiz app with Django, you need to have the following installed:


* Python 3.7 or >3.7
* Faunadb
* Django

## Installing the requirements

To install Fauna and Django you simply need to run the below command in your command-line interface.

```python
pip install faunadb
```

## Creating the Django Project

To create the Django project you need to run the command below in the command-line interface.

```python
#creating the django project

django-admin startproject Quiz

#changing directory to the django project  
cd Quiz

#creating a django app  
django-admin startapp App
```

The above command first creates a project `Quiz`, then changes the directory to the project. In the project directory, the last command creates a new app called `App`.

## The Django Project Structure

The Django project has to be structured in a particular way that follows Django’s rules and is easy for us to work with. Navigate to the `App` folder in your project, then create a new file `urls.py`. Next, in the `App` folder create two new folders `templates` and `static`. The template folder will house our HTML files, while the static folder will house our CSS, Javascript, and other static files. To do this, run the command below on your command-line.

```python
# changing directory to our app  
cd App  
# creating the static and templates folders  
mkdir templates  
mkdir static
```

The “App” folder should now look like the image below.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mtsktm390lm6i2lkmlyg.png)

Now go back to our `Quiz` directory and edit our `settings.py` file by adding our app to the list of installed apps as seen below.

```python
INSTALLED_APPS = [  
 'django.contrib.admin',  
 'django.contrib.auth',  
 'django.contrib.contenttypes',  
 'django.contrib.sessions',  
 'django.contrib.messages',  
 'django.contrib.staticfiles',  
 'App',  
]
```

Inside the `static` and `templates` folders, we are going to create their respective files.

For the `templates` folder create a new file `dashboard.html` and add the HTML code below. 

```html
{% load static %}  
  
<html>  
 <head>  
 <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">  
  
 <!-- Optional theme -->  
 <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous">  
  
 <!-- Latest compiled and minified JavaScript -->  
 <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>  
 <link href="{% static 'login/style.css' %}" rel="stylesheet"/>  
 <link href="{% static 'https://fonts.googleapis.com/css?family=Ubuntu' %}" rel="stylesheet"/>  
 <meta content="width=device-width, initial-scale=1" name="viewport"/>  
 <link href="{% static 'path/to/font-awesome/css/font-awesome.min.css' %}" rel="stylesheet"/>  
 <title>  
 QUIZ APP  
 </title>  
 </head>  
 <body>  
 <div class="main">  
 <p align="center" class="sign">  
 Welcome {{user}}  
 </p>  
 <center>  
 <form class="form1">  
 {% if messages %}  
<ul class="messages">  
 {% for message in messages %}  
 <li{% if message.tags %} class="{{ message.tags }}"{% endif %} style="color:red;">{{ message }}</li>  
 {% endfor %}  
</ul>  
{% endif %}  
 <a href="quiz"><button type="button" class="btn btn-rounded btn-primary" style="background-color:purple !important;">START QUIZ</button></a>  
 <br>  
 </form>  
 <form class="form1">  
 <a href="create-quiz"><button type="button" class="btn btn-rounded btn-primary" style="background-color:purple !important;">CREATE QUIZ</button></a>  
 <br>  
 </form>  
 <form class="form1">  
 <a href="create-questions"><button type="button" class="btn btn-rounded btn-primary" style="background-color:purple !important;">CREATE QUESTION</button></a>  
 <br>  
</form>  
 <form class="form1">  
 <a href="login"><button type="button" class="btn btn-rounded btn-primary" style="background-color:purple !important;">LOGOUT</button></a>  
 </form>  
 </center>  
 </div>  
 </body>  
</html>
```

This `dashboard.html` file is the dashboard page where a user can easily navigate between the site’s functions. 

Next, create a `login.html` file, edit, and add the following HTML code as seen below.

```html
{% load static %}  
  
<html>  
 <head>  
 <link href="{% static 'login/style.css' %}" rel="stylesheet"/>  
 <link href="{% static 'https://fonts.googleapis.com/css?family=Ubuntu' %}" rel="stylesheet"/>  
 <meta content="width=device-width, initial-scale=1" name="viewport"/>  
 <link href="{% static 'path/to/font-awesome/css/font-awesome.min.css' %}" rel="stylesheet"/>  
 <title>  
 QUIZ APP  
 </title>  
 </head>  
 <body>  
 <div class="main">  
 <p align="center" class="sign">  
 Hello   
 </p>  
 <form class="form1" method="POST">  
 {% csrf_token %}  
 {% if messages %}  
<ul class="messages">  
 {% for message in messages %}  
 <li{% if message.tags %} class="{{ message.tags }}"{% endif %} style="color:red;">{{ message }}</li>  
 {% endfor %}  
</ul>  
{% endif %}  
 <input align="center" class="un" placeholder="Username" name="username" type="text"/>  
 <input align="center" class="pass" placeholder="Password" name="password" type="password"/>  
 <button align="center" type="submit" class="submit">  
 Sign in  
 </button>  
 </form>  
 <p align="center" class="forgot">  
 <a href="register">  
 Don't have an account? Register  
 </a>  
 </p>  
 </div>  
 </body>  
</html>
```

The `login.html` page is the page where users can easily be authenticated before accessing the site’s functions. Users are required to input their username and password to login.

The next page we are going to be creating is the register page.

Create a new file `register.html`, edit and add the HTML code as seen below.

```html
{% load static %}  
  
<html>  
 <head>  
 <link href="{% static 'login/style.css' %}" rel="stylesheet"/>  
 <link href="{% static 'https://fonts.googleapis.com/css?family=Ubuntu' %}" rel="stylesheet"/>  
 <meta content="width=device-width, initial-scale=1" name="viewport"/>  
 <link href="{% static 'path/to/font-awesome/css/font-awesome.min.css' %}" rel="stylesheet"/>  
 <title>  
 QUIZ APP  
 </title>  
 </head>  
 <body>  
 <div class="main">  
 <p align="center" class="sign">  
 Create Account  
 </p>  
 <form class="form1" method="POST">  
 {% if messages %}  
<ul class="messages">  
 {% for message in messages %}  
 <li{% if message.tags %} class="{{ message.tags }}"{% endif %} style="color:red;">{{ message }}</li>  
 {% endfor %}  
</ul>  
{% endif %}  
 {% csrf_token %}  
 <input align="center" class="un" placeholder="Username" name="username" type="text"/>  
 <input align="center" class="un" placeholder="email" name="email" type="email"/>  
 <input align="center" class="pass" placeholder="Password" name="password" type="password"/>  
 <button align="center" class="submit" type="submit">  
 Register  
 </button>  
 <center>  
 <a href="login">  
 Already have an account? Login  
 </a>  
 </center>  
 </form>  
 </div>  
 </body>  
</html>
```

The `register.html` page is where users can create new accounts to use for login authentication. Users are required to enter their username, email, and password. 

The next page is the `quiz.html` page where users can see all the quizzes created. Create the `quiz.html` file and add the below code.

```html
{% load static %}  
  
<html>  
 <head>  
 <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">  
  
 <!-- Optional theme -->  
 <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous">  
  
 <!-- Latest compiled and minified JavaScript -->  
 <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>  
 <link href="{% static 'login/style.css' %}" rel="stylesheet"/>  
 <link href="{% static 'https://fonts.googleapis.com/css?family=Ubuntu' %}" rel="stylesheet"/>  
 <meta content="width=device-width, initial-scale=1" name="viewport"/>  
 <link href="{% static 'path/to/font-awesome/css/font-awesome.min.css' %}" rel="stylesheet"/>  
 <title>  
 QUIZ APP  
 </title>  
 </head>  
 <body>  
 <div class="main">  
 <p align="center" class="sign">  
 There {% if count == 1 %} is {{count}} quiz {% else %} are {{count}} quizes {% endif %} available  
 </p>  
 <p align="center" class="sign">  
 Choose a Quiz To Take  
 </p>  
 <center>  
 <div class="card" style="width: 18rem;">  
 <div class="card-body">  
 </div>  
 <br>  
</div>  
<a class="btn btn-primary">{{quiz.name}}</a>  
 <nav aria-label="...">  
 <br>  
 <p><b>About Test</b></p>  
 <p>{{quiz.description}}</p>  
 <br>  
 <p>Total Questions: {{quiz.total_questions}} </p>  
 <br>  
 <a href="answer-quiz/{{quiz.name}}" class="btn btn-danger">Start Test</a>  
 <br>  
 <ul class="pagination">  
 <li class="page-item disabled">  
 <a class="page-link" href="?page={{ prev_page }}" tabindex="-1">Previous</a>  
 </li>  
 <li class="page-item">  
 <a class="page-link" href="?page={{next_page }}">Next</a>  
 </li>  
 </ul>  
</nav>  
</center>  
 </div>  
 </body>  
</html>  
```


The next page we are going to create is the `create_quiz.html` page where users can create new quizzes for users to answer. Create the `create_quiz.html` page and add the code below.

```html
{% load static %}  
  
<html>  
 <head>  
 <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">  
  
 <!-- Optional theme -->  
 <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous">  
  
 <!-- Latest compiled and minified JavaScript -->  
 <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>  
 <link href="{% static 'login/style.css' %}" rel="stylesheet"/>  
 <link href="{% static 'https://fonts.googleapis.com/css?family=Ubuntu' %}" rel="stylesheet"/>  
 <meta content="width=device-width, initial-scale=1" name="viewport"/>  
 <link href="{% static 'path/to/font-awesome/css/font-awesome.min.css' %}" rel="stylesheet"/>  
 <title>  
 QUIZ APP  
 </title>  
 </head>  
 <body>  
 <div class="main">  
 <p align="center" class="sign">  
 Create a Quiz  
 </p>  
 <center>  
 <form class="form1" method="POST">  
 {% csrf_token %}  
 {% if messages %}  
<ul class="messages">  
 {% for message in messages %}  
 <li{% if message.tags %} class="{{ message.tags }}"{% endif %} style="color:red;">{{ message }}</li>  
 {% endfor %}  
</ul>  
{% endif %}  
 Quiz Name:  
 <input align="center" class="pass" type="text" name="quiz_name" />  
 <br>  
 Quiz Description:  
 <input align="center" class="pass" type="text" name="quiz_description" />  
 <br>  
 Total Questions:  
 <input align="center" class="pass" type="number" name="total_questions" />  
 <br>  
 <a href="dashboard" class="btn btn-primary">Back</a><button class="btn btn-primary" type="submit">Create Quiz </button>  
 </form>  
 </center>  
 </div>  
 </body>  
</html>
```

We also need to create the `answer_quiz.html` page where users can answer the quiz that they picked on the `quiz.html` page we created earlier. Copy and paste the code below in your `answer_quiz.html` file.

```html
{% load static %}  
  
<html>  
 <head>  
 <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">  
  
 <!-- Optional theme -->  
 <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous">  
  
 <!-- Latest compiled and minified JavaScript -->  
 <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>  
 <link href="{% static 'login/style.css' %}" rel="stylesheet"/>  
 <link href="{% static 'https://fonts.googleapis.com/css?family=Ubuntu' %}" rel="stylesheet"/>  
 <meta content="width=device-width, initial-scale=1" name="viewport"/>  
 <link href="{% static 'path/to/font-awesome/css/font-awesome.min.css' %}" rel="stylesheet"/>  
 <title>  
 QUIZ APP  
 </title>  
 </head>  
 <body>  
 <div class="main">  
 <p align="center" class="sign">  
 Choose Any Option from A to D  
 </p>  
 <center>  
 <div class="card" style="width: 18rem;">  
 <div class="card-body">  
 </div>  
 <br>  
</div>  
<a class="btn btn-primary">SCIENCE TEST</a>  
{% if messages %}  
<ul class="messages">  
{% for message in messages %}  
<li{% if message.tags %} class="{{ message.tags }}"{% endif %} style="color:red;">{{ message }}</li>  
{% endfor %}  
</ul>  
{% endif %}  
 {% if score %}  
 <h1>Total Score: {{score}} out of {{question_count}}<h1>  
 {% else %}  
 <nav aria-label="...">  
 <br>  
 <p><b>Question: {{question.question_asked}}</b></p>  
 <br>  
 <form method="POST" >  
 {% csrf_token %}  
 <p>A. {{question.answer_1}} <input type="radio" name="answer" value="{{question.answer_1}}" /></p>  
 <p>B. {{question.answer_2}} <input type="radio" name="answer" value="{{question.answer_2}}" /></p>  
 <p>C. {{question.answer_3}} <input type="radio" name="answer" value="{{question.answer_3}}" /></p>  
 <p>D. {{question.answer_4}} <input type="radio" name="answer" value="{{question.answer_4}}" /></p>  
 <input type="hidden" name="question" value="{{question.question_asked}}"/>  
  
 <ul class="pagination">  
 <li class="page-item disabled">  
 <a class="page-link" href="?page={{ prev_page }}" tabindex="-1">Previous</a>  
 </li>  
 <li class="page-item">  
 <a class="page-link" href="?page={{next_page }}">Next</a>  
 </li>  
 </ul>  
</nav>  
<button class="btn btn-danger" type="submit">Save Answer</button>  
</form>  
{% if finish %}  
<a href="?finish=true"><button class="btn btn-danger" >Finish</button></a>  
{% endif %}  
{% endif %}  
 <a href="{% url 'App:dashboard' %}" class="btn btn-primary">Back</a>  
</center>  
 </div>  
 </body>  
</html>
```

The final page is the `create_questions.html` page where the user can create the questions that will appear under a quiz. To create copy and paste the code below.

```html
{% load static %}  
  
<html>  
 <head>  
 <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">  
  
 <!-- Optional theme -->  
 <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous">  
  
 <!-- Latest compiled and minified JavaScript -->  
 <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>  
 <link href="{% static 'login/style.css' %}" rel="stylesheet"/>  
 <link href="{% static 'https://fonts.googleapis.com/css?family=Ubuntu' %}" rel="stylesheet"/>  
 <meta content="width=device-width, initial-scale=1" name="viewport"/>  
 <link href="{% static 'path/to/font-awesome/css/font-awesome.min.css' %}" rel="stylesheet"/>  
 <title>  
 QUIZ APP  
 </title>  
 </head>  
 <body>  
 <div class="main">  
 <p align="center" class="sign">  
 Create a Question  
 </p>  
 <center>  
 <form class="form1" method="POST">  
 {% csrf_token %}  
 {% if messages %}  
<ul class="messages">  
 {% for message in messages %}  
 <li{% if message.tags %} class="{{ message.tags }}"{% endif %} style="color:red;">{{ message }}</li>  
 {% endfor %}  
</ul>  
{% endif %}  
 Quiz Name:  
 <select align="center" class="pass" name="quiz_name">  
 {% for quiz in quiz_all %}  
 <option>{{quiz.data.name}}</option>  
 {% endfor %}  
 </select>  
 <br>  
 Question:  
 <input align="center" class="pass" type="text" name="question" />  
 <br>  
 Answer 1:  
 <input align="center" class="pass" type="text" name="answer_1" />  
 <br>  
 Answer 2:  
 <input align="center" class="pass" type="text" name="answer_2" />  
 <br>  
 Answer 3:  
 <input align="center" class="pass" type="text" name="answer_3" />  
 <br>  
 Answer 4:  
 <input align="center" class="pass" type="text" name="answer_4" />  
 <br>  
 Correct Answer:  
 <input align="center" class="pass" type="text" name="correct_answer" />  
 <br>  
 <a href="dashboard" class="btn btn-primary">Back</a><button class="btn btn-primary" type="submit">Create Question </button>  
 </form>  
 </center>  
 </div>  
 </body>  
</html>
```

We are done creating the HTML files needed. Now we need to create our CSS files. Navigate back to the `static` folder, create a new folder in it; `Login` folder.

In the `Login` folder, create a new file `style.css` and add the CSS code as seen in the GitHub gist link [here](https://gist.github.com/Chukslord1/ba193db23c75bb20c414ee190db6a472).

We are done with the `static` and `templates` folders. Let’s now edit the `urls.py` and the `views.py` file. In the `urls.py` file, copy and add the following python code as seen below.

```python
from django.conf import settings  
from django.conf.urls.static import static  
from django.urls import path, include  
from . import views  
app_name = "App"  
  
urlpatterns = [  
 path("", views.login, name="login"),  
 path("login", views.login, name="login"),  
 path("dashboard", views.dashboard, name="dashboard"),  
 path("quiz", views.quiz, name="quiz"),  
 path("register", views.register, name="register"),  
 path("create-quiz", views.create_quiz, name="create-quiz"),  
 path("create-questions", views.create_question, name="create-question"),  
 path("answer-quiz/<slug>", views.answer_quiz, name="answer-quiz"),  
]
```

In the `urls.py` file we imported the required Django modules needed and defined all the URLs we are going to be making use of in this project and connected them with the required view function needed for them to run.

```python
from django.shortcuts import render,redirect  
from django.contrib import messages  
from django.core.paginator import Paginator  
from django.http import HttpResponseNotFound  
from faunadb import query as q  
import pytz  
from faunadb.objects import Ref  
from faunadb.client import FaunaClient  
import hashlib  
import datetime  

  
  
  
client = FaunaClient(secret="SECRET_KEY")  
indexes = client.query(q.paginate(q.indexes()))
```

In the `views.py` file we imported all the required Django modules and initialized the Fauna client using our secret key which we saved earlier.

Navigate back to the base directory and open the `Quiz` folder as seen in the image below.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7hh6klvswe7zy3kq9w7q.png)

```python
from django.contrib import admin  
from django.urls import path, include  
from django.conf import settings  
from django.contrib.staticfiles.urls import staticfiles_urlpatterns  
from django.contrib.staticfiles.urls import static  
  
urlpatterns = [  
 path('admin/', admin.site.urls),  
 path('', include("App.urls")),  
  
]  
```

In the base project’s `urls.py` we imported the modules required and linked the app URLs to our base path URLs.

# Building Our Quiz App Logic

We would be going through all the steps to build the functionality of our quiz application.

## Register Functionality

The first action the user takes when opening the web app is to register if they haven't already. Update the register function in the `views.py` with the python code below.

```python
def register(request):
   if request.method == "POST":
       username = request.POST.get("username").strip().lower()
       email = request.POST.get("email").strip().lower()
       password = request.POST.get("password")

       try:
           user = client.query(q.get(q.match(q.index("users_index"), username)))
           messages.add_message(request, messages.INFO, 'User already exists with that username.')
           return redirect("App:register")
       except:
           user = client.query(q.create(q.collection("users"), {
               "data": {
                   "username": username,
                   "email": email,
                   "password": hashlib.sha512(password.encode()).hexdigest(),
                   "date": datetime.datetime.now(pytz.UTC)
               }
           }))
           messages.add_message(request, messages.INFO, 'Registration successful.')
           return redirect("App:login")
   return render(request,"register.html")
```

In the `register` view function, we first checked if the request sent from the register page is a POST request else we simply render the register page. If this was the case, then we are to expect some values (i.e the username, email, and password) in the POST request. We then make a request to Fauna with the `get` method of the FQL client and the `users_index` we created earlier to check the user's collection if a user with the match of the username already exists. If the user exists then a “User already exists with that username” message is displayed. If the user does not exist, a request is made with the FQL client to Fauna to create a document with provided details along with the date of registration. The user is now redirected to the login page to log in with his/her newly created login details. If you check the `users` collection you should see the data added as seen below.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/89fmdiyvyhzmmisdk6by.png)

On unsuccessful registration, a message as seen in the image below should be displayed.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yigzmudy9gpigqg5bz8g.png)

## Login Functionality

The second action the user takes when he opens the web app is to provide his login details for authentication. Update the login function in the `views.py` with the following python code to implement login functionality.

```python
def login(request):
   if request.method == "POST":
       username = request.POST.get("username").strip().lower()
       password = request.POST.get("password")

       try:
           user = client.query(q.get(q.match(q.index("users_index"), username)))
           if hashlib.sha512(password.encode()).hexdigest() == user["data"]["password"]:
               request.session["user"] = {
                   "id": user["ref"].id(),
                   "username": user["data"]["username"]
               }
               return redirect("App:dashboard")
           else:
               raise Exception()
       except:
           messages.add_message(request, messages.INFO,"You have supplied invalid login credentials, please try again!", "danger")
           return redirect("App:login")
   return render(request,"login.html")
```

In the `login` views function, we first check if the request sent from the login page is a POST request else we simply render the login page. If this was the case, then we are to expect some values (i.e the username and password) in the POST request. After collecting the username and password, we then make use of the `get` method of the FQL client and the `users_index` we created earlier to make a request to Fauna to check if the user exists using the username as a match. If this user exists, we then check if the password passed is correct by hashing it and comparing it to the hashed password on Fauna. If the password is correct we then store the user’s data in the session and redirect him/her to the dashboard. If the user does not exist or the password doesn’t match, then we pass a “you have supplied invalid login credentials” message to the login page then redirect the user back to the login page. We then cached our function with a try-except to handle any errors that may come from querying the Fauna.

If the login was unsuccessful you should get a result as seen in the image below.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/id803ccg8iqkkx94gyf2.png)



## The Dashboard

Update your `views.py` file with the code below to create the dashboard view and render the logged in users data on the frontend.

```python
def dashboard(request):
   if "user" in request.session:
       user=request.session["user"]["username"]
       context={"user":user}
       return render(request,"dashboard.html",context)
   else:
       return HttpResponseNotFound("Page not found")
```

On successful login, you should be redirected to the dashboard page as seen in the image below.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r39n44py7ndhyaz9bn7w.png)

## Create Quiz Functionality

We need to allow users to create new quizzes to participate in. To do this update your `views.py` file with the following code:

```python
def create_quiz(request):
   if request.method=="POST":
       name=request.POST.get("quiz_name")
       description=request.POST.get("quiz_description")
       total_questions=request.POST.get("total_questions")
       try:
           quiz = client.query(q.get(q.match(q.index("quiz_index"), name)))
           messages.add_message(request, messages.INFO, 'A Quiz with that name already exists.')
           return redirect("App:create-quiz")
       except:
           quiz = client.query(q.create(q.collection("Quiz"), {
               "data": {
                   "status":"active",
                   "name": name,
                   "description": description,
                   "total_questions": total_questions,
               }
           }))
           messages.add_message(request, messages.INFO, 'Quiz Created Successfully.')
           return redirect("App:create-quiz")
   return render(request,"create_quiz.html")
```

In the code above, we checked if there a POST request was received. If it was, we collected the “name”,”description” and “total_questions” which are fields required to create a quiz. WE then used a “try-except” to cache querying our `Quiz` collection with our `quiz_index` index which checks if any other quiz in the database matches the name provided for the quiz to be created. If the query produces no results, we then allow the quiz to be created.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/z3z8w1g263p2v3zjl7i4.png)

## Quiz Page Functionality

The quiz page carries all the quizzes created and allows users to scroll through them and choose anyone to participate in. Update your `views.py` file with the following code:

```python
def quiz(request):
   try:
       all_quiz=client.query(q.paginate(q.match(q.index("quiz_get_index"), "active")))["data"]
       quiz_count=len(all_quiz)
       page_number = int(request.GET.get('page', 1))
       quiz = client.query(q.get(q.ref(q.collection("Quiz"), all_quiz[page_number-1].id())))["data"]
       context={"count":quiz_count,"quiz":quiz, "next_page": min(quiz_count, page_number + 1), "prev_page": max(1, page_number - 1)}
       return render(request,"quiz.html",context)
   except:
       return render(request,"quiz.html")
```

In the code above, we made a query to the FQL client and created the query `all_quiz` using the `quiz_get_index` index to check for quizzes that are active. We then created a pagination `quiz` to query all data in the `all_quiz` query we created earlier using the page number requested by the user and rendered all the pagination and other required data using the context.’

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3xiw7cxsk9ciky8ig734.png)

 As seen in the image above, the user can view all the quizzes on every page and click on `Start Test` to answer a quiz of his/her choice.

## Create Questions Functionality

This functionality allows users to create questions that would appear in the different quizzes created. Update your `views.py` with the code below.

```python
def create_question(request):
   quiz_all=client.query(q.paginate(q.match(q.index("quiz_get_index"), "active")))
   all_quiz=[]
   for i in quiz_all["data"]:
       all_quiz.append(q.get(q.ref(q.collection("Quiz"),i.id())))
   context = {"quiz_all":client.query(all_quiz)}
   if request.method=="POST":
       quiz_name=request.POST.get("quiz_name")
       question_asked=request.POST.get("question")
       answer_1=request.POST.get("answer_1")
       answer_2=request.POST.get("answer_2")
       answer_3=request.POST.get("answer_3")
       answer_4=request.POST.get("answer_4")
       correct_answer=request.POST.get("correct_answer")
       try:
           question_create = client.query(q.get(q.match(q.index("question_index"), question_asked)))
           messages.add_message(request, messages.INFO, 'This question already exists')
           return redirect("App:create-question")
       except:
           question_create = client.query(q.create(q.collection("Question"), {
               "data": {
                   "quiz_name": quiz_name,
                   "question_asked": question_asked,
                   "answer_1": answer_1,
                   "answer_2": answer_2,
                   "answer_3": answer_3,
                   "answer_4": answer_4,
                   "correct_answer": correct_answer,
               }
           }))
           messages.add_message(request, messages.INFO, 'Question Created Successfully.')
           return redirect("App:create-question")
   return render(request,"create-questions.html",context)
```

In the code above, we made a query to the FQL client using the `quiz_get_index` to check query all the data in the `Quiz` collection to be rendered on the `create-questions.html` page. We then appended all the data in the query above into the `all_quiz` list. At this point, we checked if a POST request was received. If it was, we collected all the data provided in the POST request required to create a question and made a query using the `question_index` index to check if there are any documents in our `Question` collection with the same data provided for the question to be created. If there was no match, we proceeded to create the question and rendered a “Question created successfully” message to the frontend while redirecting back to the `create-questions.html` page to avoid duplicate data on reload of the page.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8pbx4k9x9zzvykh632b5.png)

## The Answer Quiz Functionality

To allow users to answer questions and get scored on a quiz, update your `views.py` with the following code:

```python
def answer_quiz(request,slug):
   question_all=client.query(q.paginate(q.match(q.index("question_get_index"), slug)))["data"]
   question_count=len(question_all)
   page_number = int(request.GET.get('page', 1))
   question = client.query(q.get(q.ref(q.collection("Question"), question_all[page_number-1].id())))["data"]
   if page_number==question_count:
       context={"question":question,"next_page": min(question_count, page_number + 1), "prev_page": max(1, page_number - 1),"finish":"true"}
   else:
       context={"question":question,"next_page": min(question_count, page_number + 1), "prev_page": max(1, page_number - 1)}
   if request.method=="POST":
       answer=request.POST.get("answer")
       question=request.POST.get("question")
       try:
           check_answer=client.query(q.get(q.match(q.index("answer_get_index"), request.session["user"]["username"],question)))["data"]
           messages.add_message(request, messages.INFO, 'You already answered this question')
       except Exception:
           answer_create = client.query(q.create(q.collection("Answers"), {
               "data": {
                   "user": request.session["user"]["username"],
                   "quiz": slug,
                   "question": question,
                   "answer": answer,
               }
           }))
           messages.add_message(request, messages.INFO, 'Answer Saved')
   if request.GET.get("finish")=="true":
       score=0
       check_answer=client.query(q.paginate(q.match(q.index("answer_score_index"), request.session["user"]["username"],slug)))
       all_answer=[]
       for i in check_answer["data"]:
           all_answer.append(q.get(q.ref(q.collection("Answers"),i.id())))
       answers=client.query(all_answer)
       for i in answers:
           try:
               mark_answer=client.query(q.get(q.match(q.index("question_answer"),i["data"]["answer"])))
               score=score+1
           except:
               score=score
       context={"score":score,"question_count":question_count}
   return render(request,"answer_quiz.html",context)
```

In the code above, we made a query `question_all` to get the quiz matching the slug passed for the particular page. We then counted the number of questions in the query `question_count` and created pagination of the `Question` collection by matching the id of the page of the `question_all` query requested by the user. We then checked if the current page number is equal to the last page in our query. If this is the case, we render “finish” to the context which will allow the user to click on the finish button and end the quiz. We also checked if a POST request was received to make an answer submission. If a POST was received, we check if the user already answered that particular question by making a query to the FQL client using the `answer_get_index` index to match the username of the current user and the question answered in the `Answers`. If the user hasn’t answered this question, we create a query to create this answer in the `Answers` collection. The user selects the answer for the current question and clicks on the `save answer button` before proceeding to the next question. Lastly, we checked if the user clicked on the `finish` button by checking if “finish==true”. We set the original score in the test and created a query `check_answer` to the FQL client to get the answers matching the particular quiz answered using the `answer_score_index` index created earlier. We then ran a for loop to append all the data in `check_answer` into the `all_answer` list. At this point, we created a query using `all_answer`. We then looped through `all_answer` to check if the answers saved by the users match the `correct_answer` field for each data in the `Question` collection using the `question_answer` index. We cached this checking query with a “try-except” so that for each time both answers match without any errors the score increase by one (1). Otherwise, the score remains the same. This loop runs matches all answers provided by the user till the final score is obtained. We then render this score to the frontend using context for the user to see.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/85umj9p5c00gggqjcx9m.png)
After picking an option for each question the user has to click on THE `Save Answer` button to save the answer to the database and click on `Next` to proceed to the next question.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3zbradlo0bo22ezmkowo.png)

As seen in the image above, after answering the last question, the user can save his answer and click on the `Finish` button to end the quiz and see his total score for that quiz.

The score will be displayed similar to the one in the image below.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bgo1uxkuxwh3v5afe52u.png)

# Conclusion

In this article, we built a quiz application with [Fauna's serverless database](https://fauna.com/) and Django. We saw how easy it is to integrate Fauna into a Python application and got the chance to explore some of its core features and functionalities.

The source code of our quiz application is available on [Github](https://github.com/Chukslord1/FuanaQuizApp). If you have any questions, don't hesitate to contact me on Twitter: [@](https://twitter.com/LordChuks3)[LordChuks3](https://twitter.com/LordChuks3).