How to create interactive charts in Django with Plotly
Build interactive Gantt charts in Django using Plotly and pandas. Step-by-step tutorial covering models, views, templates, and data visualization setup.
Plotly is an open-source Python library for rendering interactive charts in the browser. It supports over 40 chart types covering statistical, financial, geographic, scientific, and 3D use cases, and it integrates cleanly with Django through its offline rendering mode.
This tutorial covers integrating Plotly into a Django application by building a project tracker that visualizes task timelines as an interactive Gantt chart.
The full source code is on GitHub.
Prerequisites
- Python 3 installed
- A basic understanding of Django
Set up the project
Create a virtual environment to isolate dependencies from your global Python installation.
python -m venv env
Activate it:
source env/bin/activate
Install Django:
pip install django
Create a new Django project:
django-admin startproject django_plotly
Navigate into the project directory and create a Django app:
cd django_plotly
django-admin startapp charts
Add the app to INSTALLED_APPS in settings.py:
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'charts',
]
Install Plotly and pandas:
pip install plotly pandas
Create the model
The model tracks project tasks with a name, start date, finish date, assigned user, and week number.
In charts/models.py:
from django.db import models
from django.contrib.auth.models import User
class Chart(models.Model):
name = models.CharField(max_length=200)
start_date = models.DateField()
responsible = models.ForeignKey(User, on_delete=models.CASCADE)
week_number = models.CharField(max_length=2, blank=True)
finish_date = models.DateField()
def __str__(self):
return str(self.name)
def save(self, *args, **kwargs):
if self.week_number == "":
self.week_number = self.start_date.isocalendar()[1]
super().save(*args, **kwargs)
The save override automatically calculates the ISO week number from start_date if none is provided.
Register the model in charts/admin.py:
from django.contrib import admin
from .models import Chart
admin.site.register(Chart)
Run migrations:
python manage.py makemigrations
python manage.py migrate
Create a superuser to access the Django admin:
python manage.py createsuperuser
Start the server and log in at http://localhost:8000/admin:
python manage.py runserver

Create the view
The view queries all chart entries, formats them as a pandas DataFrame, and passes a rendered Plotly timeline to the template.
Create django_plotly/views.py:
from django.shortcuts import render
from charts.models import Chart
import pandas as pd
from plotly.offline import plot
import plotly.express as px
def index(request):
qs = Chart.objects.all()
projects_data = [
{
'Project': x.name,
'Start': x.start_date,
'Finish': x.finish_date,
'Responsible': x.responsible.username
} for x in qs
]
df = pd.DataFrame(projects_data)
fig = px.timeline(
df, x_start="Start", x_end="Finish", y="Project", color="Responsible"
)
fig.update_yaxes(autorange="reversed")
gantt_plot = plot(fig, output_type="div")
context = {'plot_div': gantt_plot}
return render(request, 'index.html', context)
What each part does:
Chart.objects.all()retrieves every project entry from the database- The list comprehension shapes the queryset into a format pandas can consume
px.timelinegenerates the Gantt chart —x_startandx_enddefine the horizontal axis,ysets the row labels, andcolorassigns distinct colors per responsible userplot(fig, output_type="div")renders the chart as an HTML<div>string, which the template injects directly into the page
Create the templates
Create a templates directory in the project root. Inside it, add two files: base.html and index.html.
base.html provides the page skeleton with Bootstrap for basic styling:
<!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">
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
<title>Plotly Chart</title>
</head>
<body>
<div class="container mt-3">
{% block content %}
{% endblock content %}
</div>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>
index.html renders the Plotly chart div:
{% extends "base.html" %}
{% block content %}
{% autoescape off %}
{{ plot_div }}
{% endautoescape %}
{% endblock content %}
Update TEMPLATES in settings.py to point to the templates directory:
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [ BASE_DIR / 'templates' ],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
Configure URLs
Update django_plotly/urls.py:
from django.contrib import admin
from django.urls import path
from .views import index
urlpatterns = [
path('admin/', admin.site.urls),
path('', index)
]
Add data and test
In the Django admin, add a few project entries with different users, start dates, and finish dates.


Start the server:
python manage.py runserver
Visit http://localhost:8000 to see the interactive Gantt chart with each project plotted as a timeline bar, color-coded by the responsible user.

Where to go next
This tutorial covers the core integration pattern. From here you can extend the project with date range filters, user authentication for the chart view, or additional Plotly chart types. The Plotly Python documentation covers all 40+ chart types with examples.
FAQs
1, What is Plotly and how does it work with Django?
Plotly is a Python library that generates interactive, browser-based charts. In Django, you generate a chart figure using Plotly's Python API, render it as an HTML <div> string using plotly.offline.plot with output_type="div", and pass that string to a Django template. The template injects it directly into the page with {% autoescape off %}.
2, What is a Gantt chart used for?
A Gantt chart visualizes a project schedule by plotting tasks as horizontal bars along a timeline. Each bar shows a task's start and end date. They are commonly used in project management to track task progress and resource assignment across time.
3, What does output_type="div" do in Plotly?
Setting output_type="div" in plotly.offline.plot returns the chart as a self-contained HTML string instead of opening it as a file. This string includes the chart's JavaScript and can be injected directly into a Django template, which is what makes offline Plotly rendering work in server-side web frameworks.
4, Why do I need pandas to use Plotly in Django?
Plotly's high-level plotly.express functions like px.timeline expect data as a pandas DataFrame. Pandas handles the data structuring so Plotly can map columns directly to chart axes and attributes. For simple datasets you can also pass dictionaries or lists directly to lower-level Plotly graph objects without pandas.
5, Can I use Plotly with Django REST Framework?
Yes. You can generate Plotly figures server-side and return the chart JSON using fig.to_json(), then render it on the frontend using Plotly.js. This approach works well when your frontend is a JavaScript SPA consuming a DRF API.