DEV Community

Chukslord
Chukslord

Posted on • Edited on

How I Built a Resume Builder with Django and Fauna

Authored in connection with the Write With Fauna program.

For some time now, I have been learning about Fauna and exploring its serverless database management system by building various web applications with it. In this tutorial, I will walk you through a step-by-step guide that shows how I made a resume builder with Django and Fauna.

Prerequisites

To fully understand this tutorial, you are required to have the following in place:

  • Python 3.7 or newer.
  • Basic understanding of Fauna.
  • Basic understanding of Django.
  • A text editor.

Setting Up Fauna Database

To make use of Fauna, I had to set up required collections, indexes, and API keys. Below are the steps I took to achieve this:

Creating the Database

I created a database on Fauna’s dashboard for the project. To do this, login to your dashboard, click the New Database button and provide the required information.

Creating the Collections

I needed to create collections for storing our data in Fauna. Collections are like tables in a standard database system. I created two collections for the resume builder project, which are Users (storing registered users) and Resume_Info collection (storing information used to generate the user’s resume).

To create collections in Fauna, navigate to the Collections tab in Fauna’s dashboard side menu, click on the New Collection button and provide the information required as seen in the images below:

The TTL field is where you specify how long you want the data in the respective collections to last, while the History Days stores historical records. For this project, I used the default value of 30 for History days and left the TTL blank.

Creating the Fauna Indexes

I needed to create indexes for browsing through data in the database collections. I created two indexes for this project, one for each collection named users_index and resume_index. I then set appropriate Terms for them.

To create an index, navigate to the Indexes tab in Fauna’s dashboard side menu, click on the New Index button and provide the information required as seen in the images below:

For the users_index, I set the connected collection to Users, and the Terms for browsing data with this index is the username field. The Terms are fields in the collection that Fauna will match when querying a collection with an index.

For the resume_index, I set the connected collection to Resume_Info and the Terms to user. Tick the unique checkbox for both indexes before saving to specify that all data queried is unique.

Creating the Database Security Key

To enable Django to interact with Fauna, a secret key is required by the Fauna Driver. To do this, navigate to the Security tab in Fauna’s dashboard side menu, click on the New Key button and provide the information required as seen in the images below:

After generating the secret key, you will be presented with a screen like the one above (hidden here as “SECRET_KEY” for privacy). Copy your secret key from the dashboard and save it somewhere you can easily retrieve it for later use.

Creating The Resume Builder Django Project

To install Django and Fauna, simply run the command below:


pip install django

pip install faunadb

Enter fullscreen mode Exit fullscreen mode

At this point, I created the Django project using the commands below:


# create the django project

django-admin startproject RESUME_BUILDER

# change directory to the django project

cd RESUME_BUILDER

# create the django app

django-admin startapp App

Enter fullscreen mode Exit fullscreen mode

In the command above, I first created a project called RESUME_BUILDER, then changed the directory to the Django project. In the project directory, I created a new app called App.

I then edited my settings.py file in my project folder and added the following code. Navigate to the RESUME_BUILDER directory and edit our settings.py file by adding our app to the list of installed apps as seen below:


INSTALLED_APPS = [

 'django.contrib.admin',

 'django.contrib.auth',

 'django.contrib.contenttypes',

 'django.contrib.sessions',

 'django.contrib.messages',

 'django.contrib.staticfiles',

 'App'

]

Enter fullscreen mode Exit fullscreen mode

I also defined the static and media paths at the end of the settings.py file as seen in the code below:


STATIC_URL = '/static/'

STATIC_ROOT=os.path.join(BASE_DIR, 'static')

MEDIA_ROOT =os.path.join(BASE_DIR, 'media')

MEDIA_URL = '/media/'

Enter fullscreen mode Exit fullscreen mode

Next, I edited the urls.py file in the project folder with the code below, which links it to my app’s URLs.


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")),


]
urlpatterns += staticfiles_urlpatterns()
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

Enter fullscreen mode Exit fullscreen mode

Finally, I made migrations to save and migrate all settings to the database. Run the code below in your command-line to make migrations.


python manage.py migrate

Enter fullscreen mode Exit fullscreen mode

The Django Project Structure

I needed to structure the Django project in a particular way that follows Django’s rules and will make it easy to work with during this tutorial. To do this, navigate to the App folder in your project, then create a new file, urls.py.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.


#change directory to our App

cd App

mkdir templates

mkdir static

Enter fullscreen mode Exit fullscreen mode

These commands will structure the App folder as seen in the image below:

Defining the URL routes

A urls.py in the App folder will contain all defined URL routes for our app. To do this, create a urls.py file and add the code as seen below:


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("register", views.register, name="register"),
    path("index", views.index, name="index"),
    path("create-resume", views.create_resume, name="create-resume"),
    path("resume", views.resume, name="resume"),


]

Enter fullscreen mode Exit fullscreen mode

Building the User Interface

The user interface entails all the HTML, CSS, and Javascript files required to build a user-friendly interactive web application.

Creating the Static Files

I created two new files in the static folder, styles.css and script.js. I then updated them with their respective CSS and Javascript codes as seen in the Github Gist here.

The Login Page

Users need to be authenticated to access the web application. In the templates folder, I created a new file named login.html and updated it with the code below:


{% load static %}

<!DOCTYPE html>

<html lang="en">

 <head>

 <meta charset="utf-8"/>

 <title>

 Fauna Resume Builder

 </title>

 <link href="{% static './style.css' %}" rel="stylesheet"/>

 </head>

 <body>

 <!-- partial:index.partial.html -->

 <div id="javascript_header">

 </div>

 <form method="POST">

 {% csrf_token %}

 <div class="form__header">

 <h1>

 Login/Sign In

 </h1>

 <p>

 Enter your login details below.

 </p>

 {% csrf_token %}

<ul class="messages">

 {% for message in messages %}

 <li{% if message.tags %} class="{{ message.tags }}"{% endif %} style="color:red;">{{ message }}</li>

 {% endfor %}

</ul>

 </div>

 <div class="form-group">

 <label for="name">

 Username

 <span>

 *

 </span>

 </label>

 <input id="username" name="username" type="text"/>

 <div class="error" id="name__error">

 </div>

 </div>

 <div class="form-group">

 <label for="password">

 Password

 <span>

 *

 </span>

 </label>

 <input id="password" name="password" type="password"/>

 </div>

 <div class="line-break">

 </div>

 <input id="login" type="submit" value="Login"/>

 Don't have an account? <a href="register">Sign Up</a>

 </form>

 <!-- partial -->

 <script src="{% static './script.js' %}">

 </script>

 </body>

</html>

Enter fullscreen mode Exit fullscreen mode

The Register Page

For users to successfully authenticate on the login page, they need to create login details on the register page. To achieve this, in the templates folder, I created a register.html file and added the below HTML code:


{% load static %}

<!DOCTYPE html>

<html lang="en">

 <head>

 <meta charset="utf-8"/>

 <title>

 Fauna Resume Builder

 </title>

 <link href="{% static './style.css' %}" rel="stylesheet"/>

 </head>

 <body>

 <!-- partial:index.partial.html -->

 <div id="javascript_header">

 </div>

 <form method="POST">

 {% csrf_token %}

 <div class="form__header">

 <h1>

 Register/Sign Up

 </h1>

 <p>

 Enter your login details below.

 </p>

 </div>

 <div class="form-group">

 <label for="name">

 Username

 <span>

 *

 </span>

 </label>

 <input id="username" name="username" type="text"/>

 <div class="error" id="name__error">

 </div>

 </div>

 <div class="form-group">

 <label for="email">

 Email

 <span>

 *

 </span>

 </label>

 <input id="email" name="email" type="email"/>

 <div class="error" id="name__error">

 </div>

 </div>

 <div class="form-group">

 <label for="password">

 Password

 <span>

 *

 </span>

 </label>

 <input id="password" name="password" type="password"/>

 </div>

 <div class="line-break">

 </div>

 <input id="register" type="submit" value="Register"/>

 Already have an account? <a href="login">Sign In</a>

 </form>

 <!-- partial -->

 <script src="{% static './script.js' %}">

 </script>

 </body>

</html>

Enter fullscreen mode Exit fullscreen mode

The Dashboard Page

This page is where users can access the functionalities of the resume builder easily. To achieve this, I created an index.html file in the templates folder and added the below HTML code:


{% load static %}

<!DOCTYPE html>

<html lang="en">

 <head>

 <meta charset="utf-8"/>

 <title>

 Fauna Resume Builder

 </title>

 <link href="{% static './style.css' %}" rel="stylesheet"/>

 </head>

 <body>

 <!-- partial:index.partial.html -->

 <div id="javascript_header">

 </div>

 <form>

 <div class="form__header">

 <h1>

 Build Your Resume

 </h1>

 <p>

 Dashboard

 </p>

 </div>

 <div class="line-break">

 </div>

 <a class="button" href="create-resume"> Create/Edit Resume</a>

 <div class="line-break">

 </div>

 <a class="button" href="resume"> View Resume</a>

 <div class="line-break">

 </div>

 <a class="button" style="background-color:red;" href="login"> Logout </a>

 </form>

 <!-- partial -->

 <script src="{% static './script.js' %}">

 </script>

 </body>

</html>

Enter fullscreen mode Exit fullscreen mode

The Create Resume Page

On this page, users submit their resume info for their resume to be generated. To achieve this page, I created a create-resume.html page in the templates folder and added the code below:


{% load static %}

<!DOCTYPE html>

<html lang="en">

 <head>

 <meta charset="utf-8"/>

 <title>

 Fauna Resume Builder

 </title>

 <link href="{% static './style.css' %}" rel="stylesheet"/>

 </head>

 <body>

 <!-- partial:index.partial.html -->

 <div id="javascript_header">

 </div>

 <form method="POST">

 {% csrf_token %}

 <ul class="messages">

 {% for message in messages %}

 <li{% if message.tags %} class="{{ message.tags }}"{% endif %} style="color:red;">{{ message }}</li>

 {% endfor %}

 </ul>

 <div class="form__header">

 <h1>

 Build Your Resume

 </h1>

 <p>

 Enter the fields below to generate a resume file.

 </p>

 </div>

 <h2>

 Personal Details

 </h2>

 <div class="form-group">

 <label for="name">

 Full Name

 <span>

 *

 </span>

 </label>

 <input id="name" name="name" value="{{resume_info.full_name}}" type="text"/>

 <div class="error" id="name__error">

 </div>

 </div>

 <div class="form-group">

 <label for="address">

 Address

 </label>

 <input id="address" name="address" value="{{resume_info.address}}" type="text"/>

 </div>

 <div class="form-group">

 <label for="phone">

 Phone

 </label>

 <input id="phone" name="phone" value="{{resume_info.phone}}" type="text"/>

 </div>

 <div class="form-group">

 <label for="email">

 Email

 <span>

 *

 </span>

 </label>

 <input id="email" name="email" value="{{resume_info.email}}" type="email"/>

 <div class="error" id="email__error">

 </div>

 </div>

 <div class="form-group">

 <label for="about">

 About You

 </label>

 <textarea id="about" name="about" placeholder="{{resume_info.about_you}}"></textarea>

 </div>

 <div class="form-group">

 <label for="career">

 Career Objectives

 </label>

 <textarea id="career" name="career" placeholder="{{resume_info.career}}"></textarea>

 </div>

 <div class="form-group">

 <label for="education">

 Education

 </label>

 <textarea id="education" name="education" placeholder="{{resume_info.education}}"></textarea>

 </div>

 <div class="line-break">

 </div>

 <h2>

 Work Experience

 </h2>

 <h3>

 Most Recent Job

 </h3>

 <div class="form-date-group">

 <div class="form-group">

 <label for="job-1__start">

 Start Date

 </label>

 <input id="job-1__start" name="job-1__start" value="{{resume_info.job_1__start}}" type="date"/>

 </div>

 <div class="form-group">

 <label for="job-1__end">

 End Date

 </label>

 <input id="job-1__end" name="job-1__end" value="{{resume_info.job_1__end}}" type="date"/>

 </div>

 </div>

 <div class="form-group">

 <label for="job-1__details">

 Position Details

 </label>

 <textarea id="job-1__details" name="job-1__details" placeholder="{{resume_info.job_1__details}}"></textarea>

 </div>

 <div class="line-break">

 </div>

 <h3>

 Past Job

 </h3>

 <div class="form-date-group">

 <div class="form-group">

 <label for="job-2__start">

 Start Date

 </label>

 <input id="job-2__start" name="job-2__start" value="{{resume_info.job_2__start}}" type="date"/>

 </div>

 <div class="form-group">

 <label for="job-2__end">

 End Date

 </label>

 <input id="job-2__end" name="job-2__end" value="{{resume_info.job_2__end}}" type="date"/>

 </div>

 </div>

 <div class="form-group">

 <label for="job-2__details">

 Position Details

 </label>

 <textarea id="job-2__details" name="job-2__details" placeholder="{{resume_info.job_2__details}}"></textarea>

 </div>

 <div class="line-break">

 </div>

 <h3>

 Another Past Job

 </h3>

 <div class="form-date-group">

 <div class="form-group">

 <label for="job-3__start">

 Start Date

 </label>

 <input id="job-3__start" name="job-3__start" value="{{resume_info.job_3__start}}" type="date"/>

 </div>

 <div class="form-group">

 <label for="job-3__end">

 End Date

 </label>

 <input id="job-3__end" name="job-3__end" value="{{resume_info.job_3__end}}" type="date"/>

 </div>

 </div>

 <div class="form-group">

 <label for="job-3__details">

 Position Details

 </label>

 <textarea id="job-3__details" name="job-3__details" placeholder="{{resume_info.job_3__details}}"></textarea>

 </div>

 <div class="line-break">

 </div>

 <div class="form-group">

 <label for="references">

 References

 </label>

 <textarea id="references" name="references" placeholder="{{resume_info.references}}"></textarea>

 </div>

 <div class="line-break">

 </div>

 <input type="submit" value="Submit Info"/>

 <br>

 <a class="button"style="background-color:red;" href="index">Back to home</a>

 </form>

 <!-- partial -->

 <script src="{% static './script.js' %}">

 </script>

 </body>

</html>

Enter fullscreen mode Exit fullscreen mode

The Resume Page

The resume page is where the generated resume generated would be displayed for the user to view. To achieve this page, I created a new file resume.html and added the code below:


<html>

<head>

 <title>{% if resume_info.full_name %}{{resume_info.full_name}}'s Resume {% endif %}</title>

 <style>

@import url('https://fonts.googleapis.com/css?family=Poppins:400,600&display=swap');

body {

 font-family: 'Poppins', sans-serif;

 background: #fafafa;

 color: rgba(0,0,0,0.75);

}

h1 {

 color: rgba(0,0,0,0.9);

}

h1, p {

 box-sizing: border-box;

 margin: 0px;

 padding: 0px 24px;

}

.line-break {

 width: 100%;

 height: 1px;

 margin: 16px auto;

 border-bottom: 1px solid #eee;

}

.resume {

 border-radius: 8px;

 box-sizing: border-box;

 display: flex;

 flex-direction: column;

 max-width: 800px;

 margin: 48px auto;

 padding: 16px 0px;

 background: white;

 box-shadow: 0 1px 3px rgba(0, 0, 0, 0.02), 0 1px 2px rgba(0, 0, 0, 0.14);

}

.resume-group {

 box-sizing: border-box;

 padding: 8px 0px;

 width: 100%;

 display: flex;

 border-left: 3px solid transparent;

 transition: 0.2s;

}

.resume-group:hover {

 border-left: 3px solid #64FFDA;

}

.left-col {

 width: 35%;

}

.right-col {

 width: 65%;

}

.instructions {

 opacity: 0.5;

 text-align: center;

 font-size: 0.8rem;

 margin: 16px auto;

}

</style>

</head>

<body>

 <div class="resume">

 {% if resume_info.full_name %}

 <h1>{{resume_info.full_name}}</h1>

 {% endif %}

 {% if resume_info.email %}

 <p>{{resume_info.email}}</p>

 {% endif %}

 {% if resume_info.phone %}

 <p>{{resume_info.phone}}</p>

 {% endif %}

 {% if resume_info.address %}

 <p>{{resume_info.address}}</p>

 {% endif %}

 <div class="line-break"></div>

 {% if resume_info.about_you%}

 <div class="resume-group">

 <div class="left-col"><p>ABOUT ME</p></div>

 <div class="right-col">

 <p>{{resume_info.about_you}}

</p>

</div>

</div>

{% endif %}

{% if resume_info.career %}

<div class="resume-group"><div class="left-col">

 <p>CAREER OBJECTIVES</p>

</div>

<div class="right-col">

 <p>{{resume_info.career}}</p>

</div>

</div>

{% endif %}

{% if resume_info.education %}

<div class="resume-group">

 <div class="left-col">

 <p>EDUCATION</p>

 </div>

 <div class="right-col">

 <p>{{resume_info.education}}</p>

</div>

</div>

{% endif %}

<div class="resume-group">

 <div class="left-col">

 <p>EMPLOYMENT EXPERIENCE</p>

 </div>

 <div class="right-col">

 <p></p>

 </div>

</div>

{% if resume_info.job_1__start %}

<div class="resume-group">

 <div class="left-col">

 <p>{{resume_info.job_1__start}} to {{resume_info.job_1__end}}</p>

 </div>

 <div class="right-col">

 <p>{{resume_info.job_1__details}}

</p>

</div>

</div>

{% endif %}

{% if resume_info.job_2__start %}

<div class="resume-group">

 <div class="left-col">

 <p>{{resume_info.job_2__start}} to {{resume_info.job_2__end}}</p>

 </div>

 <div class="right-col">

 <p>{{resume_info.job_2__details}}

</p>

</div>

</div>

{% endif %}

{% if resume_info.job_3__start %}

<div class="resume-group">

 <div class="left-col">

 <p>{{resume_info.job_3__start}} to {{resume_info.job_3__end}}</p>

 </div>

 <div class="right-col">

 <p>{{resume_info.job_3__details}}

</p>

</div>

</div>

{% endif %}

<div class="resume-group">

 <div class="left-col">

 <p>REFERENCES</p>

 </div>

 <div class="right-col">

 <p>{% if resume_info.references %}{{resume_info.references}}{% else %}Available upon request{% endif %}</p>

 </div>

</div>

</div>

<div class="instructions">Ctrl + P and "Save As PDF" to make a copy of this resume</div>

</body>

</html>

Enter fullscreen mode Exit fullscreen mode

Building the Functionalities

We would now be focusing on how I created the app logic. In Django, the logic is built majorly in the views.py file in the App folder.

Importing the Required Modules

Firstly, I imported the modules required for this project to work in the views.py file as seen in the code below:


from django.shortcuts import render,redirect
from django.contrib import messages
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

Enter fullscreen mode Exit fullscreen mode

Initializing the FQL Client and Index

To allow the python app to interact with Fauna easily, I needed to initialize the client and indexes using the secret key I saved earlier. To do this, I added the code below to the views.py file.


client = FaunaClient(secret="SECRET_KEY")

indexes = client.query(q.paginate(q.indexes()))

Enter fullscreen mode Exit fullscreen mode

The Login Functionality

To create the logic for the login page, I added the code below to my views.pyas seen below:


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:index")
            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")


Enter fullscreen mode Exit fullscreen mode

In the code above, I checked if a POST request was received. If one was received, I collected the required data expected to be obtained from the POST request, which is the username and the password of the user. I then used the data stored in the username variable as a matching parameter for the FQL client to utilize in querying the Users collection for a user matching the data in the username field using the users_index index. If the user existed, I checked if the password for that users matched the one provided. If the passwords matched, the user is successfully authenticated, and the username is stored in the session. However, if the user does not exist or the passwords provided don’t match, then a message, “You have supplied invalid login credentials” is rendered.

The Register Functionality

To create the logic for the register page, I added the code below to my views.pyas seen below:


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")

Enter fullscreen mode Exit fullscreen mode

In the code above, I checked if a POST request was received. If one was received, I then collected the required data expected to be obtained from the POST request, which is the user’s username, email, and password. I then used the data stored in the username as a matching parameter for the FQL client to utilize in querying the Users collection for a user matching the data in the username field using the users_index index. If the user existed, a message, “ User already exists with that username” is rendered. However, if there is no match in the database, an account with the required info is created in the database by making a query to the Users collection using the FQL client.

The Dashboard

For the dashboard, no logic is required. I simply rendered the “index.html” file with the code below:


def index(request):
    return render(request,"index.html")

Enter fullscreen mode Exit fullscreen mode

The Create Resume Functionality

I added the code seen below to the views.py file to implement the resume creation logic for the create resume functionality.


def create_resume(request):
    if request.method=="POST":
        username=request.session["user"]["username"]
        full_name=request.POST.get("name")
        address=request.POST.get("address")
        phone=request.POST.get("phone")
        email=request.POST.get("email")
        about_you=request.POST.get("about")
        education=request.POST.get("education")
        career=request.POST.get("career")
        job_1__start=request.POST.get("job-1__start")
        job_1__end=request.POST.get("job-1__end")
        job_1__details=request.POST.get("job-1__details")
        job_2__start=request.POST.get("job-2__start")
        job_2__end=request.POST.get("job-2__end")
        job_2__details=request.POST.get("job-2__details")
        job_3__start=request.POST.get("job-3__start")
        job_3__end=request.POST.get("job-3__end")
        job_3__details=request.POST.get("job-3__details")
        references=request.POST.get("references")
        try:
            resume = client.query(q.get(q.match(q.index("resume_index"), username)))
            quiz = client.query(q.update(q.ref(q.collection("Resume_Info"),resume["ref"].id()), {
                "data": {
                    "user":username,
                    "full_name": full_name,
                    "address": address,
                    "phone": phone,
                    "email":email,
                    "about_you":about_you,
                    "education":education,
                    "career":career,
                    "job_1__start":job_1__start,
                    "job_1__end":job_1__end,
                    "job_1__details":job_1__details,
                    "job_2__start":job_2__start,
                    "job_2__end":job_2__end,
                    "job_2__details":job_2__details,
                    "job_3__start":job_3__start,
                    "job_3__end":job_3__end,
                    "job_3__details":job_3__details,
                }
            }))
            messages.add_message(request, messages.INFO, 'Resume Info Edited Successfully. Download Resume Now')
            return redirect("App:create-resume")
        except:
            quiz = client.query(q.create(q.collection("Resume_Info"), {
                "data": {
                    "user":username,
                    "full_name": full_name,
                    "address": address,
                    "phone": phone,
                    "email":email,
                    "about_you":about_you,
                    "education":education,
                    "job_1__start":job_1__start,
                    "job_1__end":job_1__end,
                    "job_1__details":job_1__details,
                    "job_2__start":job_2__start,
                    "job_2__end":job_2__end,
                    "job_2__details":job_2__details,
                    "job_3__start":job_3__start,
                    "job_3__end":job_3__end,
                    "job_3__details":job_3__details,
                }
            }))
            messages.add_message(request, messages.INFO, 'Resume Info Saved Successfully. Download Resume Now')
            return redirect("App:resume")
    else:
        try:
            resume_info = client.query(q.get(q.match(q.index("resume_index"), request.session["user"]["username"])))["data"]
            context={"resume_info":resume_info}
            return render(request,"create-resume.html",context)
        except:
            return render(request,"create-resume.html")


Enter fullscreen mode Exit fullscreen mode

In the code above, I checked if a POST request was received. If one was received, I collected the required data from it and used it to generate the user’s resume. I then checked if the user has already submitted resume information earlier by using the username of the user stored in the session as a matching parameter for the FQL client to utilize in querying the database for data that match the user field of the Resume_Info collection. If the user didn’t have any resume information saved before, then documents will be created using the resume information collected. However, if the user has saved his/her resume information earlier, then the resume info is updated using the new resume_info provided. I also rendered the resume info data for the user in the respective field to the create resume page using context.

The Resume Page Functionality

The only functionality this page has is to render already saved data on the resume page.

I achieved this by making a query using the FQL to get the resume info for the current user by using the username as a match for the user field in the Resume_Info collection. I then passed the data retrieved to context which will be rendered on the resume page. To achieve this I added the code below and add to my views.py file as seen below:


def resume(request):
    try:
        resume_info = client.query(q.get(q.match(q.index("resume_index"), request.session["user"]["username"])))["data"]
        context={"resume_info":resume_info}
        return render(request,"resume.html",context)
    except:
        return render(request,"resume.html")

Enter fullscreen mode Exit fullscreen mode

Finally, I succeeded in building my resume builder web application. If you followed through with this tutorial, run your article by executing the below command.


python manage.py runserver

Enter fullscreen mode Exit fullscreen mode

You can now view your application on your localhost

Conclusion

In this article, I walked you through a step-by-step guide showing how I built a resume builder using Fauna and Django. We saw how easy it is to integrate Fauna into a Python/Django application and got our hands dirty by exploring some of its core features and functionalities.

The source code of the resume builder application is available on Github, and you can access a live demo here. If you have any questions, don't hesitate to contact me on Twitter: @LordChuks3.

Top comments (11)

Collapse
 
vindceaw profile image
vindceaw

Hi. for def login(request), is there any error in the below?

except: try again!", "danger")
return re
messages.add_message(request, messages.INFO,"You have supplied invalid login credentials!”, pleasedirect("App:login")
return render(request,"login.html")

i was trying out but kept having error

Collapse
 
chukslord1 profile image
Chukslord

Thanks for the correction. I have updated the code .

Collapse
 
nitinsaini18 profile image
NitinSaini18 • Edited

Hi. This issue is not solved yet. please check it again. I have also tried this project from GitHub also but the same issue I faced. I traced it but not able to find any error. During register time it is registering very well stored data on the database also. but while login it is not picking my user name. please check it out from your side.

Thread Thread
 
chukslord1 profile image
Chukslord

Hello, I have made sure to check the python code properly. Please also make sure you are passing the data correctly from your HTML pages to your views. You can check the github repo here github.com/Chukslord1/FAUNA_RESUME... and the live link here faunaresumebuilder.larridscare.com... if there's an issue on it similar to the one your experiencing.

Collapse
 
garg573 profile image
Gaurav Garg

Can we connect mysql db instead of faundaDB, If yes then please guide me how can we do that .

Collapse
 
chukslord1 profile image
Chukslord

contact me on twitter @LordChuks3

Collapse
 
tlcpack profile image
Taylor Cooke

Is Fauna required for this? Could you run this with a standard django db? or something like postgres?

Collapse
 
chukslord1 profile image
Chukslord

Yes you can build the resume builder with a regular database

Collapse
 
mohd_adil_3bf4f287221548a profile image
Mohd Adil

app module not found

Collapse
 
mohd_adil_3bf4f287221548a profile image
Mohd Adil

seee

Collapse
 
yokeyokesh profile image
YOKEYOKESH

hi , demo link is not working , its showing account suspended, please re fix it.