How I Built an OpenAI-Powered Web Assistant with Django

Django is a backend web framework that makes it easy to build web pages quickly using Python. There is no better way to learn Django than by building projects.

This tutorial shows you how I built an Artificial Intelligence-powered web assistant using Django. I’ll also add a GitHub link so you can copy&paste (see conclusion).

Set Up

To get started, we will create a directory where every file will live in. In the directory, we will create and activate a virtual environment. Then we will install the required Python libraries for the project. I am using an Ubuntu terminal, so a basic knowledge of the command line will be an added advantage going forward.

mkdir project && cd project
python3 -m venv .venv
source .venv/bin/activate

In the project directory, we create and activate a virtual environment using the source command. You can also replace the source command with a dot .. Let’s now install the modules we will be using.

pip install django tzdata openai

Creating Django Project

Once the installation is complete, run the following command in your Ubuntu terminal to create a Django project.

django-admin startproject webassistant .

This creates a folder with the name webassistant.

  • The . tells Django to create the project in the current directory.
  • The manage.py file is used to execute several Django commands.
  • The settings.py in the webassistant folder is the project’s settings. In it, we will register the Django apps we are about to create.
  • The urls.py is where we will let Django know what it should display to the user.

We now check to ensure that the installation went successfully. In your terminal run the following command:

python3 manage.py runserver

Once you have seen the above image, congrats! You have successfully installed Django. You can use control C to close the server.

Creating Django Apps

Back to your terminal, run the following command to create a Django app.

python3 manage.py startapp assistant

Use the ls command to see what’s inside the assistant folder.

ls assistant
__init__.py  admin.py  apps.py migrations  models.py  tests.py views.py

The __init__.py file found in both the webassistant and assistant folders enables the folders to be imported as a Python package. The views.py is where we code what we want the browser to be displayed to the user. These files are what concern our project. To know more about other files, check the documentation.

Next, we go to the settings.py file, in INSTALLED_APPS section to register the name of the app we just created. Use the nano command.

nano webassistant/settings.py

...
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    # custom app
    'assistant',
]

We also open the project’s urls.py file to register the app-level URLs.

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('assistant.urls')),
]

The path() function is used to map the URL to the appropriate view. The include() function adds the URL of the app to the project-level urls.py. The empty quote means the home URL, that is, what we see when we run the local server.

If you have read Django tutorials including this one, you are gradually becoming familiar with the process. That’s how it is done in every Django application.

Getting the API Key

We need an API key to enable the OpenAI model to perform web assistant tasks for us. To get the API key, we first have to create an account on the official website of OpenAI. Once you have completed the signup process, go to the OpenAI API reference where you will be directed to a page to generate your API key.

Recommended: OpenAI API – or How I Made My Python Code Intelligent

Make sure you keep the API key safe. Create a file in your app-level folder and call it key.py.

API_KEY = 'YOUR SECRET API KEY'

Just replace the text in quotes with your own generated API key.

Integrating the OpenAI Model

To integrate the API with our Django application, create a file called engine.py in the app’s folder and input the following python script.

# engine.py

from .key import API_KEY
import openai

openai.api_key = API_KEY

def model(request):
    prompt = request.POST.get('prompt')
    response = openai.Completion.create(
        engine='text-davinci-003',
        temperature=0.5
        prompt=prompt,
        max_tokens=1000,
    )
    text = response.choices[0].text
    chats = {'prompt':  prompt,
                 'response': text
                }
    return chats

We import the API key and the openai module. We use the openai.api_key to load the API key. Then, in the function, we requested to get the prompt, which is the question asked by the user. We then return the response generated by the model in form of a dictionary.

The temperature affects the randomness of the output, and it’s between 0 and 1. The AI model employed to generate predictions is the text_davinci_003. The max_tokens specifies the maximum number of tokens or pieces of words that can be generated by the model.

To learn more about the parameters, perhaps this article can be of help. We will now import the function in our views.py file.

from django.shortcuts import render, redirect
from .engine import model

def home(request):
    try:
        if request.method == 'POST':
            context = model(request)
            return render(request, 'home.html', context)
        else:
            return render(request, 'home.html')
    except:
        return redirect('error')

def error_handler(request):
        return render(request, 'error.html')

Two functions indicate two separate HTML files. In the first function, we use a try statement to check the block of code for errors. If no errors were found, the code under the try statement will execute. But if there were errors, the code under the except statement will be executed.

🐍 Recommended: Python Try/Except Error Handling

The if statement checks if the request method is POST, if so, it will generate a response from the OpenAI model. But if otherwise, the else statement will be run in which no response will be generated.

The render() function renders or displays a response in the HTML files which we are yet to create. Notice that in the else statement, the render() function just renders the same homepage without the context because the request method was not POST. The redirect() function is used to redirect a user to another webpage.

Let’s now write a URL in the urls.py file to display our contents.

assistant/urls.py

from django.urls import path
from .import views

urlpatterns = [
    path('', views.home, name='home'),
    path('error', views.error_handler, name='error_handler'),
]

The name argument is kind of an alias for the URL. So instead of writing long URLs, we can just reference them with the name given. Mostly used in HTML files.

Templates

We now want to render our templates. Create a folder named templates in the current directory. This is where we will keep our HTML files. Having created the folder, go to settings.py and let Django know that a templates folder is created.

In the settings.py file, scroll down to the ‘TEMPLATES’ section and add the following to DIRS.

…
TEMPLATES = [
    {
        …
        'DIRS': [os.path.join(BASE_DIR, 'templates')],
        …
    }
]

Be sure to import the os module. Then, create a file in the templates folder with the name base.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Web Assistant | {% block title %}  {% endblock %}</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
    {% block content %}
    {% endblock %}
</body>
</html>

That’s our HTML boilerplate with bootstrap added to it for styling our web pages. Next is the home.html, the homepage that will inherit everything in the base.html template.

{% extends 'base.html' %}
{% block title %} Home {% endblock %}
{% block content %}
<div class="row justify-content-center my-4">
    <div class="col-md-7 mt-4">
        <div class="card">
            <h1 class="card-header text-center">A.I WEB ASSISTANT</h1>
            <div class="card-body">
             <pre>Hello, how can I help you?</pre>
              <form action="." method="POST">
                <!-- this secures the form from malicious attacks during submission -->
                {% csrf_token %}
                <input class="form-control mb-2" required type="text" autofocus="autofocus" name="prompt" value="{{ prompt }}" id="">
                <button class="btn btn-success fw-bold" type="submit">
                     GENERATE
                </button>
              </form>
              <hr>
              <pre>
              {{ response }}      
              </pre>
            </div>
        </div>
    </div>
  </div>
</div>
{% endblock %}

Finally, the error.html will be displayed when an error occurs. It also inherits everything in the base.html.

{% extends 'base.html' %}
{% block title %} 404 {% endblock %}
{% block content %}
<div class="row justify-content-center my-4">
    <div class="col-md-7 mt-4">
        <h1>Page Not Found</h1>
        <p>Make sure you are connected to the internet or your query is correct</p>
        <a href="{% url 'home' %}" class="btn btn-secondary">Home</a>
    </div>
</div>
{% endblock %}

Certain things in these HTML files demand an explanation. Those strange syntaxes that begin with curly braces are Django templating language. When used with a block statement, it must end with an endblock statement. In base.html, we inserted the empty block statement in the title tag.

This makes it possible to override the home and error HTML files with a different word. But you can see the ‘Web Assistant’ remains the same in all files inheriting base.html.

The csrf_token is for security reasons. It’s compulsory. If you don’t add it, Django will throw an error. The prompt variable comes from the view.py file which in turn is imported from the engine.py file. The same applies to the response. Remember, we sent them here using the render() function.

The {% url 'home' %} syntax is Django’s way of displaying internal URLs. Go back to the app-level urls.py, you will see where we defined the name and this makes it possible to use it in HTML files.

Conclusion

Congrats on creating an AI-powered web assistant using Django. If you enjoy the tutorial, feel free to share it with others. You can also download the code from GitHub here. Have a nice day.

Recommended: How I Created an URL Shortener App Using Django