Building an Appointment Scheduler App with Django and Fauna
Tags: fauna, beginners, python, webdev
Published: 2021-02-11 08:31:37

If you have ever wanted to build an appointment scheduling application with Django and have been looking to explore Fauna serverless database then this article is for you. Personally, I have always had problems with remembering meetings and appointments I have planned out for the day. I decided to build a solution to this problem, with the appointment scheduling application I can easily check appointments I have for the day.

To build this, I made use of the Django Python web framework and [Fauna](https://fauna.com). Django is a very popular web framework, but not so many people know about Fauna and how amazing it can be when managing databases.

# What is Fauna?

Fauna is a client-side serverless document database that makes use of GraphQL and the Fauna Query Language (FQL) to support a variety of data types and particularly relational databases in a serverless API. You can learn more about Fauna in their official documentation [here](https://docs.fauna.com/fauna/current/).

# Creating A Fauna Database

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.

![](https://dev-to-uploads.s3.amazonaws.com/i/i1kzn37fnepv26d106n9.png)

After clicking on the button as shown in the image above, you then need to give your database a name and save it.

![](https://dev-to-uploads.s3.amazonaws.com/i/ac9ne2feugq9l698td6u.png)

![](https://dev-to-uploads.s3.amazonaws.com/i/4jotwex7ktf46pnzici9.png)

You will also need to create two collections (one for `users` and one for `events`). 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. 

![](https://dev-to-uploads.s3.amazonaws.com/i/yeao1l21pk9bkyw4rm0n.png)

After saving the collection you just created, you will be presented with a screen similar to the image above. A document is relatable to rows of data in a table like a normal database system.

# Creating a Fauna Index

We will need to create a Fauna index that allows us to scroll through data added to our database. To do this, go to the `DB Overview` tab on the left side of your screen then click on the `New Index` button.

![](https://dev-to-uploads.s3.amazonaws.com/i/udait8cf12f3v3cgtaqy.png)

![](https://dev-to-uploads.s3.amazonaws.com/i/p1nertvfskyny9f16sfz.png)

After clicking on the `New Index` button you will be presented with the above screen. You are required to select the collection you want to connect this particular index to. After choosing the collection, enter a name for your index, terms for your index, and values. Also tick the `Unique` checkbox to ensure the data entered for the username is unique. The terms field is used to specify what data you want the index to be able to browse. In this case, we are using a username as the terms. Click on save once you are done filling the required fields. We also have to create three indexes; one with the date and time attributes as unique terms of the `Events` collection, the other with user attribute as terms, and the last with user and date as terms. The indexes should be created as seen below.

![](https://dev-to-uploads.s3.amazonaws.com/i/xyrirjba19scpxvmsudm.png)

![](https://dev-to-uploads.s3.amazonaws.com/i/q4mq52202ya7is4ku124.png)

![](https://dev-to-uploads.s3.amazonaws.com/i/gxsk4x9p9cmru8sh99sl.png)

# Integrating Fauna with Python

## 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.

![](https://dev-to-uploads.s3.amazonaws.com/i/c7xdevjr5h50how9mfd2.png)

![](https://dev-to-uploads.s3.amazonaws.com/i/iefo4wf68z45w6u51521.png)

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 as seen in the image above.

![](https://dev-to-uploads.s3.amazonaws.com/i/q53jxyx0dtk5quudlbwq.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 Appointment Scheduler UI

## Prerequisites

From this point onwards, to follow this step by step guide on building an appointment 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 commands in your command-line interface.

```python
pip install django  
pip install faunadb
```

To check if Django was successfully installed run the command below in the command-line interface. This will provide you with the version of Django installed on your system.

django-admin --version

To check if Fauna installed properly, run the sample python code provided in Fauna’s Python driver [documentation](https://docs.fauna.com/fauna/current/drivers/python.html).

```python
from faunadb import query as q  
from faunadb.objects import Ref   
from faunadb.client import FaunaClient  
  
client = FaunaClient(secret="your-secret-here")  
  
indexes = client.query(q.paginate(q.indexes()))  
  
print(indexes)
```

If your result after running is similar to the image below, then you are good to go.


![](https://dev-to-uploads.s3.amazonaws.com/i/feyom37c4pg21seoymjn.png)

## Creating the Django Project

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

```
#creating the django project

django-admin startproject AppointmentScheduler

#changing directory to the django project  
cd AppointmentScheduler

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

The above command first creates a project `AppointmentScheduler`, 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.

```
# 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.

![](https://dev-to-uploads.s3.amazonaws.com/i/3yyslolvzydu0kcovw85.png)

Now go back to our `AppointmentScheduler` 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 `index.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>  
 Appointment Scheduler  
 </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="create-appointment"><button type="button" class="btn btn-rounded btn-primary" style="background-color:purple !important;">Create Appointment</button></a>  
 </form>  
 <form class="form1">  
 <a href="today-appointment"><button type="button" class="btn btn-rounded btn-primary" style="background-color:purple !important;">Appointments Today</button></a>  
 </form>  
 <form class="form1">  
 <a href="all-appointment"><button type="button" class="btn btn-rounded btn-primary" style="background-color:purple !important;">All Appointments</button></a>  
 </form>  
 </center>  
 </div>  
 </body>  
</html>
```

This `index.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>  
 Appointment Scheduler  
 </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 log in.

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>  
 Appointment Scheduler  
 </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 `today-appointment.html` page where users can view appointments they scheduled and are taking place that present day. Users can view all their and click on the “click to complete” button to complete their appointments for the day.

Create the “today-appointment” HTML file, edit, and add the following code as seen 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>  
 Appointment Scheduler  
 </title>  
 </head>  
 <body>  
 <div class="main">  
 <p align="center" class="sign">  
 You have {{count}} Appointments Today  
 </p>  
 <center>  
 <div class="card" style="width: 18rem;">  
 <div class="card-body">  
 <h5 class="card-title">{{appointment.name}}</h5>  
 <p class="card-text">{{appointment.description}}</p>  
 <p class="card-text">Date: {{appointment.date}}</p>  
 <p class="card-text">Time:{{appointment.time}}</p>  
 </div>  
 {% if appointment.status == "True" %}  
 <a class="btn btn-primary">Completed</a>  
 {% else %}  
 <a href="?complete=''&page={{page_num}}" class="btn btn-danger">Click to complete</a>  
 {% endif %}  
</div>  
 <nav aria-label="...">  
 <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>  
<a href="dashboard"> <button class="btn btn-danger btn-rounded" type="button">Back</button></a>  
<a href="?delete=''&page={{page_num}}"> <button class="btn btn-danger btn-rounded" type="button">Delete</button></a>  
</center>  
</center>  
 </div>  
 </body>  
</html>
```

The last HTML page we are going to be creating is the `all-appointments.html` page. This page is where users can view all the appointments they have created (both past and present). Users can also see the appointments they completed and those they didn’t complete. Create a new file `all-appintments.html`, edit and add the following code as seen below to it.

```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>  
 Appointment Scheduler  
 </title>  
 </head>  
 <body>  
 <div class="main">  
 <p align="center" class="sign">  
 You have {{count}} Appointments in Total  
 </p>  
 <center>  
 <div class="card" style="width: 18rem;">  
 <div class="card-body">  
 <h5 class="card-title">{{appointment.name}}</h5>  
 <p class="card-text">{{appointment.description}}</p>  
 <p class="card-text">Date: {{appointment.date}}</p>  
 <p class="card-text">Time:{{appointment.time}}</p>  
 </div>  
</div>  
{% if appointment.status == "True" %}  
<a class="btn btn-primary">Completed</a>  
{% else %}  
<a class="btn btn-danger">Not Completed</a>  
{% endif %}  
 <nav aria-label="...">  
 <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>  
<a href="dashboard"> <button class="btn btn-danger btn-rounded" type="button">Back</button></a>  
<a href="?delete=''&page={{page_num}}"> <button class="btn btn-danger btn-rounded" type="button">Delete</button></a>  
</center>  
 </div>  
 </body>  
</html>
```

We need to create a page where users can schedule/create appointments. To do this, create a new folder called `appoint`. In the `appoint` folder, create a new file `create-appointment.html`, edit, and add the following HTML code as seen below.

```html
{% load static %}  
  
<!DOCTYPE html>  
<html lang="en">  
 <head>  
 <meta charset="utf-8"/>  
 <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>  
 <title>  
 Appointment Scheduler  
 </title>  
 <link href="{% static 'appointment/style.css' %}" rel="stylesheet"/>  
 <meta content="width=device-width, initial-scale=1" name="viewport"/>  
 </head>  
 <body>  
 <!-- partial:index.partial.html -->  
 <script src="https://code.jquery.com/jquery-2.1.0.min.js">  
 </script>  
 <body>  
 <div id="formWrapper">  
 <div id="form">  
 <div class="logo">  
 <h2>Create an Appointment</h2>  
 </div>  
 <form 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 %}  
 <div class="form-item">  
 <p class="formLabel">  
 Appointment Name  
 </p>  
 <input autocomplete="off" class="form-style" id="email" name="name" type="text" required/>  
 </div>  
 <div class="form-item">  
 <p class="formLabel">  
 Appointment Description  
 </p>  
 <input autocomplete="off" class="form-style" id="email" name="description" type="text" required/>  
 </div>  
 <div class="form-item">  
 <p class="formLabel">  
 Appointment Time  
 </p>  
 <input autocomplete="off" class="form-style" id="email" name="time" type="time" required/>  
 </div>  
 <div class="form-item">  
 <p class="formLabel">  
 Appointment Date  
 </p>  
 <input autocomplete="off" class="form-style" id="email" name="date" type="date" required/>  
 </div>  
 <div class="form-item">  
 <a href="dashboard"> <button class="btn btn-danger btn-rounded" type="button">Back</button></a>  
 <input class="login pull-right" type="submit" value="Create"/>  
 <div class="clear-fix">  
 </div>  
 </div>  
 </div>  
 </form>  
 </div>  
 </body>  
 </body>  
</html>  
<!-- partial -->  
<script src="{% static 'appointment/script.js' %}">  
</script>
```

We are done creating the HTML files needed. Now we need to create our CSS files. Navigate back to the `static` folder, create two new folders in it; `appointment` and `login` folders.

In the `appointment` folder, create a new file `script.js` then add the Javascript code as seen in the GitHub gist link [here](https://gist.github.com/Chukslord1/1f00a82c975f1f0f8727414ac6026017). Create another file `style.css` and add the CSS code as seen in the GitHub gist link [here](https://gist.github.com/Chukslord1/a8a16d0766ffd743014b5f326223e8e9). Now navigate to the “login” folder, create a new file `style.css`, edit and add the following CSS code to it 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("create-appointment", views.create_appointment, name="create-appointment"),  
 path("today-appointment", views.today_appointment, name="today-appointment"),  
 path("all-appointment", views.all_appointment, name="all-appointment"),  
 path("register", views.register, name="register"),  
  
]
```

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
# Create your views here.  
from django.shortcuts import render  
  
def login(request):  
 return render(request,"login.html")  
  
def create_appointment(request):  
 return render(request,"appoint/create-appointment.html")  
  
  
  
def dashboard(request):  
 return render(request,"index.html")  
  
  
def today_appointment(request):  
 return render(request,"today-appointment.html")  
  
  
def all_appointment(request):   
 return render(request,"all-appointment.html")  
  
def register(request):  
 return render(request,"register.html")
```

In the `views.py` file we imported all the required Django modules and defined all the view functions while rendered their respective HTML pages which we are going to be adding functionalities to later in this walkthrough.

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


![](https://dev-to-uploads.s3.amazonaws.com/i/o3ddjdtn74d35w9v2dmm.png)

Open and edit the `urls.py` file and add the following python code as seen below.

```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 Appointment Scheduler Logic

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

## Importing Required Modules

Edit and import the following modules we will need for our project as seen in the python code below.

```python
# Create your views here.  
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
```

## Initializing FQL Client

We will now initialize the FQL client which we will need to access our database from time to time within this project. To initialize the FQL client copy and paste the code below immediately after your imports above.

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

Make sure to replace `SECRET_KEY` with your real secret key you stored safely earlier.

## 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 `users` 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.

![](https://dev-to-uploads.s3.amazonaws.com/i/pdv0rvilupytqaa1892m.png)

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

![](https://dev-to-uploads.s3.amazonaws.com/i/jxgn4vleuc7a717h29j3.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.

![](https://dev-to-uploads.s3.amazonaws.com/i/dfjjnhus85tiy03cqxzp.png)

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

![](https://dev-to-uploads.s3.amazonaws.com/i/x3cl0zk6blppeymxn1b7.png)

## Creating Appointment Functionality

Let’s now give our `create-appointment` page the functionality to schedule new appointments. To do this copy the python code below and update the `create_appointment` function in your views.py.

```python
def create_appointment(request):
    if "user" in request.session:
        if request.method=="POST":
            name=request.POST.get("name")
            description=request.POST.get("description")
            time=request.POST.get("time")
            date=request.POST.get("date")
            try:
                user = client.query(q.get(q.match(q.index("events_index"), date,time)))
                messages.add_message(request, messages.INFO, 'An Event is already scheduled for the specified time.')
                return redirect("App:create-appointment")
            except:
                user = client.query(q.create(q.collection("Events"), {
                    "data": {
                        "name": name,
                        "description": description,
                        "time": time,
                        "date": date,
                        "user": request.session["user"]["username"],
                        "status": 'False',
                    }
                }))
                messages.add_message(request, messages.INFO, 'Appointment Scheduled Successfully.')
                return redirect("App:create-appointment")
        return render(request,"appoint/create-appointment.html")
    else:
        return HttpResponseNotFound("Page not found")
```

In the `create_appointment` view we first checked if the user is logged in by checking if his/her data is stored in the session. If the user is not logged in then this page cannot be accessed. We then check if a POST request was sent. If this is the case, then the data sent along in the POST request (i.e name, description, time, date) is collected. A request with the FQL client is then sent to Fauna to check if the date and time of the event to be scheduled is already existing. If they exist, then an “An Event is already scheduled for the specified time” message is displayed. If the date and time are not existing in the database, then a request with the FQL client is made to create the appointment with provided details. 

If the appointment is scheduled successfully you should see an image like the one below.

![](https://dev-to-uploads.s3.amazonaws.com/i/0ogrbot0vufrmnageuvz.png)

Also if you check the `Events` collection you should see some new data as in the image below.

![](https://dev-to-uploads.s3.amazonaws.com/i/kj7vliyx3wmxt54bpq83.png)

## View Upcoming Appointments Functionality

We need to allow users to see their appointments scheduled for the current day. To do this, in the `today_appointment` function update the python code as seen below.

```python
def today_appointment(request):
   if "user" in request.session:
       appointments=client.query(q.paginate(q.match(q.index("events_today_paginate"), request.session["user"]["username"],str(datetime.date.today()))))["data"]
       appointments_count=len(appointments)
       page_number = int(request.GET.get('page', 1))
       appointment = client.query(q.get(q.ref(q.collection("Events"), appointments[page_number-1].id())))["data"]
       context={"count":appointments_count,"appointment":appointment,"page_num":page_number, "next_page": min(appointments_count, page_number + 1), "prev_page": max(1, page_number - 1)}
       return render(request,"today-appointment.html",context)
   else:
       return HttpResponseNotFound("Page not found")
```

In the `today_appointment` view, we first checked if the user is logged in by making sure the data is stored in the session. If the user is not logged in then this page cannot be accessed. We then create a query `appointments` by making a request with the `paginate` method of the FQL client to Fauna to paginate and match the data to the username of the user and the current date stored in the `Events` collection using the `events_today_paginate` index we created earlier. 

We then create a variable `appointments_count` to count the number of documents in the `appointments` query above and pass it as context to the frontend. To scroll through all the data, a GET request is sent with the page number. This page number is collected and the default is set to 1 (in case no page number is sent) and used to get the documents in the query. To do this we create a variable `appointment` and assign it to a query created by making a request with the `get` method of the FQL client to the `Events` collection and the id of the current document in the `appointments` query we created above. The `appointment`, `page_num`, `next_page`, and `prev_page` are all sent to the context to be rendered on the frontend. The `prev_page` is created by getting the maximum between 1 and the current page number to avoid going out of the index. While the `next_page` is created by getting the minimum between 1 and the current page number.

## Completing and Deleting Appointments

To complete an event scheduled for the current day, a GET request is sent by clicking on the `complete button` in the template. When this request is sent the query for the current page appointment is retrieved and updated using the `update` method of the FQL client to change the status in the document to `true`. For deleting appointments, a GET request is sent by clicking on the `delete button` in the template. When this request is sent the query for the current page appointment is retrieved and deleted using the `delete` method of the FQL client. To add these functionalities, add the following code to the `today_appointment` function as seen below.

```python
def today_appointment(request):
   if "user" in request.session:
       appointments=client.query(q.paginate(q.match(q.index("events_today_paginate"), request.session["user"]["username"],str(datetime.date.today()))))["data"]
       appointments_count=len(appointments)
       page_number = int(request.GET.get('page', 1))
       appointment = client.query(q.get(q.ref(q.collection("Events"), appointments[page_number-1].id())))["data"]
       if request.GET.get("complete"):
           client.query(q.update(q.ref(q.collection("Events"), appointments[page_number-1].id()),{"data": {"status": "True"}}))["data"]
           return redirect("App:today-appointment")
       if request.GET.get("delete"):
           client.query(q.delete(q.ref(q.collection("Events"), appointments[page_number-1].id())))
           return redirect("App:today-appointment")
       context={"count":appointments_count,"appointment":appointment,"page_num":page_number, "next_page": min(appointments_count, page_number + 1), "prev_page": max(1, page_number - 1)}
       return render(request,"today-appointment.html",context)
   else:
       return HttpResponseNotFound("Page not found")
```

The today page should be displayed as seen in the image below.

![](https://dev-to-uploads.s3.amazonaws.com/i/rmazi2r6py8vuy0coon6.png)

If you click on the `click to complete` button you should have a result as seen in the image below.

![](https://dev-to-uploads.s3.amazonaws.com/i/jbjmf812hyydppbfyfav.png)

## Viewing All Appointments

We need to allow users to see all their appointments scheduled for the current day. To do this, in the `all_appointment` function update the python code as seen below.

```python
def all_appointment(request):
   if "user" in request.session:
       appointments=client.query(q.paginate(q.match(q.index("events_index_paginate"), request.session["user"]["username"])))["data"]
       appointments_count=len(appointments)
       page_number = int(request.GET.get('page', 1))
       appointment = client.query(q.get(q.ref(q.collection("Events"), appointments[page_number-1].id())))["data"]
       if request.GET.get("delete"):
           client.query(q.delete(q.ref(q.collection("Events"), appointments[page_number-1].id())))
           return redirect("App:all-appointment")
       context={"count":appointments_count,"appointment":appointment, "next_page": min(appointments_count, page_number + 1), "prev_page": max(1, page_number - 1)}
       return render(request,"all-appointment.html",context)
   else:
       return HttpResponseNotFound("Page not found")
```

In the `all_appointment` view, we first checked if the user is logged in by confirming that data is stored in the session. If the user is not logged in then this page cannot be accessed. We then create a query `appointments` by making a request with the `paginate` method of the FQL client to Fauna to paginate and match the data to the username of the user stored in the `Events` collection using `events_index_paginate` index we created earlier. We then create a variable `appointments_count` to count the number of documents in the `appointments` query above and pass it as context to the frontend.

To scroll through all the data, a GET request is sent with the page number. This page number is collected and the default is set to 1 (in case no page number is sent), and used to get the documents in the query. To do this we create a variable `appointment` and assign it to a query created by making a request with the `get` method of the FQL client to the `Events` collection and the id of the current document in the `appointments` query we created above. 

The `appointment`, `page_num`, `next_page`, and `prev_page` are all sent to the context to be rendered on the frontend. The `prev_page` is created by getting the maximum between 1 and the current page number to avoid going out of the index. While the `next_page` is created by getting the minimum between 1 and the current page number. 

Your `all-appointment` page should look like the one in the image below.

![](https://dev-to-uploads.s3.amazonaws.com/i/bw05z81fjf93wuux6wft.png)

# Conclusion

In this article, we built an appointment scheduler 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 appointment scheduler application is available on [Github](https://github.com/Chukslord1/Fauna_Appointment_Scheduler). If you have any questions, don't hesitate to contact me on Twitter: [@](https://twitter.com/LordChuks3)[LordChuks3](https://twitter.com/LordChuks3).