~/TechPurAI
~/tutorials/ai-projects-with-python/build-an-ai-chatbot-with-django
intermediate·part 5 of 22·3 min read

Build an AI Chatbot with Django

Updated Aug 16, 2026Python · AI

Part 4 built the real integration foundation — a service module and a persistence model. This part completes it into a genuine, working Django chatbot app, following the same real app structure the Django From Scratch series established.

The real, complete app structure

text
support/
├── models.py       → part 4's ChatMessage model
├── services.py      → part 4's ask_support_assistant()
├── views.py
├── urls.py
└── templates/
    └── support/
        └── chat.html

Real, proper URL configuration

python
# support/urls.py
from django.urls import path
from . import views

urlpatterns = [
    path("support/", views.chat_view, name="support-chat"),
]
python
# project/urls.py
from django.urls import path, include

urlpatterns = [
    path("", include("support.urls")),
]

This is the exact real URL pattern from the Django series' own URLs coverage — nothing AI-specific here, deliberately, since the routing layer doesn't need to know or care that this particular view happens to call an LLM.

The real, complete view, extending part 4's minimal version

python
# support/views.py
from django.shortcuts import render
from django.contrib.sessions.backends.db import SessionStore
from .services import ask_support_assistant
from .models import ChatMessage

PLAN_DATA = """
- Gift Subscription: $18/mo, 1 bag, free shipping
- Monthly Subscription: $16/mo, 1 bag, free shipping
"""

def chat_view(request):
    if not request.session.session_key:
        request.session.create()
    session_id = request.session.session_key

    if request.method == "POST":
        question = request.POST.get("message", "").strip()
        if question:
            ChatMessage.objects.create(session_id=session_id, role="user", content=question)
            answer = ask_support_assistant(question, PLAN_DATA)
            ChatMessage.objects.create(session_id=session_id, role="assistant", content=answer)

    messages = ChatMessage.objects.filter(session_id=session_id)
    return render(request, "support/chat.html", {"messages": messages})

request.session.create() ensures a real, persistent session key exists even for a brand-new visitor — directly resolving part 4's minimal version, which assumed a session key already existed.

The real, complete template

html
<!-- support/templates/support/chat.html -->
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>Bright Leaf Coffee Support</title>
  </head>
  <body>
    <main>
      <h1>Ask us anything</h1>
      <div>
        {% for message in messages %}
          <p><strong>{{ message.role }}:</strong> {{ message.content }}</p>
        {% endfor %}
      </div>
      <form method="POST">
        {% csrf_token %}
        <label for="message">Your question</label>
        <input type="text" id="message" name="message" required />
        <button type="submit">Send</button>
      </form>
    </main>
  </body>
</html>

{% csrf_token %} is genuinely required here — exactly the same CSRF protection the Django series' own forms and CSRF coverage covers, and it applies identically to an AI-powered form as to any other real Django POST request; the AI logic behind the view changes nothing about Django's own security requirements.

Why it matters

This is genuinely why part 4 built a separate services module first — the view above is almost entirely real, standard Django (sessions, ORM queries, template rendering) with exactly one line, ask_support_assistant(...), actually touching AI logic. A developer who's never worked with an LLM API can read and maintain this entire view correctly.

Why database-backed history is a real, meaningful upgrade from part 3's Flask version

text
Flask (part 3): conversation lived in a browser cookie — gone if
  cleared, invisible server-side, size-constrained
Django (this part): conversation lives in a real database table —
  survives across devices for a logged-in real user (once tied to a
  real user account instead of just a session), inspectable for
  real support quality review, with no practical size constraint

This is a genuine, structural improvement, not just a different framework doing the same thing — persistent, queryable conversation history is what makes a real feature like "show me my last support conversation" or "flag conversations where the assistant said it didn't know the answer" actually buildable.

Next: building a real AI REST API with FastAPI — a genuinely different, API-first pattern for when the AI feature needs to serve other real applications, not render its own HTML.

VK

Vijay Kumar

Founder of TechPurAI — writing hands-on tutorials and honest tool breakdowns.

LinkedIn ↗
← previous4. Integrate an LLM API into a Django Projectnext →6. Build an AI REST API with FastAPI