Django has long been a go-to framework for developers seeking to build robust web applications.
Its templating engine allows for clean, maintainable HTML generation, while its ecosystem offers countless tools to extend functionality.
Among these tools, django-template-partials
and HTMX are a great combination for creating dynamic, interactive web applications with minimal JavaScript.
In this article, we’ll explore how django-template-partials
and HTMX can be used together to simplify the development of interactive components, improving both developer experience and end-user satisfaction.
What is django-template-partials
?
django-template-partials
is a powerful Django package designed to simplify the way templates are composed and reused.
This package introduces the concept of partials, which are reusable, encapsulated chunks of templates.
These partials help developers break down complex UIs into smaller, manageable pieces, making it easier to build and maintain web applications.
Unlike traditional template inheritance, django-template-partials
emphasizes a modular approach, where components can be independently defined and rendered.
Why Use django-template-partials
?
In a typical Django application, templates often rely on inheritance and block structures to maintain reusability.
While effective, this approach can lead to tightly coupled designs, where changes in parent templates propagate throughout the system, causing unintended side effects.
django-template-partials
addresses this by allowing developers to create isolated template fragments, or partials, that are:
- Reusable: Use the same partial in multiple templates without duplication.
- Self-Contained: Encapsulate rendering logic and context within the partial, reducing complexity in the main template.
- Composable: Combine partials to create dynamic, flexible layouts without overloading template inheritance.
Key Features
- Encapsulation: Partials allow you to encapsulate both logic and presentation into reusable units. This encapsulation improves separation of concerns, enabling developers to focus on specific parts of the UI without worrying about the larger structure.
- DRY Principle: By reusing partials, you avoid duplicating template code, making your application easier to maintain. Instead of copying similar sections across multiple templates, you define a single partial and use it wherever needed.
- Cleaner Context Management: With traditional Django templates, context variables are passed down through inheritance or shared across the entire template. This can lead to naming collisions and unnecessary clutter. Partials receive their context directly, keeping the main template’s context clean.
- Improved Testability: Since partials are self-contained, they can be tested independently, ensuring their behaviour is consistent without depending on the surrounding context.
- Seamless Integration: Partials work seamlessly with Django’s existing template system, making them easy to adopt. You can mix and match partials with traditional templates as needed.
When to Use Partials?
- Reusable Components: For UI elements like navigation menus, user cards, or notifications that appear across multiple pages.
- Dynamic Content: To render sections of a page dynamically in response to user actions, often in combination with tools like HTMX.
- Complex Layouts: To break down large, monolithic templates into smaller, manageable pieces.
- API-Driven Frontends: For rendering small HTML fragments in response to API calls, improving performance and user experience.
A Practical Comparison: Partials vs. Template Inheritance
Feature | Partials | Template Inheritance |
---|---|---|
Modularity | Highly modular; easy to reuse and test | Less modular; relies on parent-child hierarchy |
Reusability | Can be reused in any template | Tightly coupled to the inheritance structure |
Context Isolation | Context is self-contained | Context is shared across the template tree |
Dynamic Rendering | Ideal for dynamic content updates | Requires custom handling for partial updates |
What is HTMX?
HTMX is a modern, lightweight JavaScript library designed to simplify the creation of dynamic, interactive web applications.
By leveraging a declarative approach, HTMX allows developers to enhance user interactions without writing extensive custom JavaScript code.
Instead, you can achieve interactivity using HTML attributes, seamlessly integrating with server-side frameworks like Django.
HTMX follows the principle of server-driven interactivity, where the server takes responsibility for rendering HTML fragments, and the browser dynamically updates specific parts of the page.
This approach minimizes JavaScript complexity and focuses on leveraging the strengths of your server-side codebase.
Why Use HTMX?
Traditional front-end development often involves frameworks like React, Vue, or Angular, which shift much of the application logic to the client side.
While powerful, these frameworks can lead to increased complexity, larger bundle sizes, and the need for specialized knowledge.
HTMX offers a more lightweight alternative by allowing the server to remain the primary source of truth for rendering, while the client handles only the interactions.
With HTMX, you can:
- Reduce JavaScript dependency and complexity.
- Maintain server-side rendering for improved SEO and performance.
- Implement dynamic features with minimal effort, focusing on declarative HTML.
Key Features of HTMX
- Server-Driven Interactivity: HTMX shifts the responsibility for rendering HTML back to the server. Instead of managing UI state and logic on the client, you can use Django views (or any server-side logic) to generate and return HTML fragments. HTMX then swaps these fragments
- Minimal JavaScript: HTMX eliminates the need for boilerplate JavaScript. Features like live search, modals, and infinite scrolling can be implemented with just a few HTML attributes, allowing you to focus on your server-side logic.
- Declarative API: HTMX introduces a rich set of attributes for common interaction patterns, enabling you to define behaviour directly in your HTML:
hx-get
: Send a GET request to a specified URL.hx-post
: Send a POST request with form data.hx-swap
: Define how to replace content in the DOM (e.g.,innerHTML
,outerHTML
,beforeend
, etc.).hx-trigger
: Specify the event that triggers the request (e.g.,click
,keyup
,load
).hx-target
: Define which DOM element to update with the server’s response.
Additional Features
- WebSocket Support: HTMX includes built-in support for WebSockets, enabling real-time updates without third-party libraries. This makes it easy to implement features like live notifications or dashboards.
- Progress Indicators: HTMX provides built-in support for showing progress indicators during requests. Simply add a
hx-indicator
attribute to specify an element to show or hide while the request is processing. - Request Interception: HTMX supports request pre-processing through custom events, giving you fine-grained control over how requests are made or handled.
- Event System: HTMX emits a range of events during its lifecycle, enabling you to hook into specific stages of requests (e.g.,
htmx:beforeRequest
,htmx:afterSwap
).
Benefits of Using HTMX
- Performance: By swapping only the relevant parts of the DOM, HTMX reduces page reloads, improving the speed and responsiveness of your application.
- SEO-Friendly: Server-rendered HTML is easily indexable by search engines, unlike client-rendered JavaScript content.
- Simplified Workflow: Developers can focus on server-side logic and HTML without juggling complex JavaScript frameworks.
- Lightweight: At just ~10 KB (gzipped), HTMX adds minimal overhead to your project.
Building and Comparing Implementations
To properly understand the usage of django-template-partials
and it main differences with a normal Django implementation or even an HTMX implementation, we will walk through the three implementations with an example of a very simple TODO application:
- Basic Django App (
app
) - Django with HTMX (
app_htmx
) - Django with HTMX and Template Partials (
app_htmx_partials
)
In parenthesis is the name of the folder/application that you can find in the source code, which you can download in the link at the end of the article.
In the next sections I assume you have knowledge of Python and Django, that you have experience creating Django apps and know how to setup models, views and URLs.
The goal here is to demonstrate and explain the key implementation differences.
Basic Django App (app
)
This implementation represents a straightforward approach to building a TODO app using standard Django tools.
Let's quickly see the models.py
to understand the simple database model that we will use for all implementations:
from django.db import models
# Post model
class Post(models.Model):
id = models.AutoField(primary_key=True)
title = models.CharField(max_length=100)
content = models.TextField()
def __str__(self):
return self.title
Here we have a simple Post
model with id
, title
and content
.
Views (views.py
)
Let's now see how is the views implemented:
from django.shortcuts import render, redirect
from app.models import Post
# Index view
def index(request):
todos = Post.objects.all()
return render(request, 'app/index.html', {'todos': todos})
# Save view, receives a POST request
def save(request):
if request.method == 'POST':
title = request.POST['title']
content = request.POST['content']
# Save the post
post = Post(title=title, content=content)
post.save()
# Redirect to the index page
return redirect('app:index')
# Delete view, receives a GET request
def delete(request, post_id):
if request.method == 'GET':
# Delete the post
post = Post.objects.get(id=post_id)
post.delete()
# Redirect to the index page
return redirect('app:index')
This code snippet implements basic CRUD (Create, Read, Delete) functionality for a Django-based TODO application:
- Index View: Retrieves all
Post
objects (representing TODO items) from the database and renders them using theindex.html
template. - Save View: Handles form submissions via
POST
requests. It extracts thetitle
andcontent
from the request, creates a newPost
object, saves it to the database, and redirects back to the index page. - Delete View: Processes
GET
requests to delete aPost
by itsid
. After deletion, it redirects to the index page.
This structure demonstrates a typical approach to implementing views in Django, utilizing request methods to differentiate between actions and the redirect
function for navigation after changes.
Templates
Now let's see the implementation of the HTML templates:
base.html
: Provides a shared layout for the application.index.html
: Displays the TODO list and includes a form for adding new items.
base.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Django Todo App Simple</title>
</head>
<body>
{% block content %}
{% endblock %}
</body>
</html>
This is a basic HTML template for a Django application. It includes a <!DOCTYPE>
declaration, a <head>
section with character encoding and a title, and a <body>
section. The {% block content %}
placeholder is used for injecting content from child templates, following Django's template inheritance system.
index.html
{% extends 'app/base.html' %}
{% block content %}
<h1>Todo App Simple App</h1>
<form method="POST" action="{% url 'app:save' %}">
{% csrf_token %}
<input type="text" name="title" placeholder="Title" required>
<input type="text" name="content" placeholder="Content" required>
<button type="submit">Add</button>
</form> <ul> {% for todo in todos %}
<li>
{{ todo.title }} - {{ todo.content }} | <a href="{% url 'app:delete' todo.id %}">Delete</a>
</li> {% endfor %}
</ul>
{% endblock %}
This Django template extends a base template (app/base.html
) and defines the content
block. It renders a simple TODO app interface with the following elements:
- Title: Displays "Todo App Simple App" as a header.
- Form: A form for adding new TODO items, with fields for
title
andcontent
, protected by a CSRF token and submitting to thesave
view. - TODO List: Iterates through
todos
and displays each item'stitle
andcontent
in a list. Each item includes a delete link that triggers thedelete
view for the corresponding todo.
Workflow
This app relies on traditional Django rendering techniques:
- When a user adds or deletes a TODO item, the page reloads entirely.
- All data is fetched from the database and passed to the template for re-rendering.
- While simple and functional, this approach may feel slow and inefficient for users since every action disrupts the page flow.
Running example
Let's see this example in action:
Django with HTMX (app_htmx
)
Let's now see a more modern approach that builds upon the basic app by integrating HTMX, a modern library that allows seamless, dynamic updates to parts of a webpage.
Views (views.py
)
from django.shortcuts import render, redirect
from app_htmx.models import Post
# Index view
def index(request):
todos = Post.objects.all()
return render(request, 'app_htmx/index.html', {'todos': todos})
# Save view, receives a POST request
def save(request):
if request.method == 'POST':
title = request.POST['title']
content = request.POST['content']
# Save the post
post = Post(title=title, content=content)
post.save()
# Get all posts
todos = Post.objects.all()
# Redirect to the index page
return render(request,'app_htmx/list.html', {'todos': todos})
# Delete view, receives a GET request
def delete(request, post_id):
if request.method == 'GET':
# Delete the post
post = Post.objects.get(id=post_id)
post.delete()
# Get all posts
todos = Post.objects.all()
# Redirect to the index page
return render(request, 'app_htmx/list.html', {'todos': todos})```
This code defines three Django views for a TODO application using HTMX for dynamic updates:
- Index View: Fetches all
Post
objects (TODOs) and renders the mainindex.html
template. - Save View: Handles
POST
requests to save a new TODO item. After saving, it fetches all TODOS and renders the updated TODO list using thelist.html
partial template, which is dynamically updated via HTMX. - Delete View: Handles
GET
requests to delete a specific TODO item by itsid
. After deletion, it fetches all TODOs and renders the updated TODO list using thelist.html
partial template for HTMX-based dynamic updates.
Templates
This app separates the TODO list into a partial template (list.html
) to facilitate targeted updates:
base.html
: Base layout remains unchanged, but there is now an import to the HTMX library.index.html
: Uses HTMX attributes to load and update the TODO list dynamically.list.html
: Contains the TODO list markup and is included dynamically via HTMX.
base.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Django Todo HTMX Simple</title>
<script src="https://unpkg.com/htmx.org@2.0.4"></script>
</head>
<body>
{% block content %}
{% endblock %}
</body>
</html>
This HTML template serves as the base layout for a Django app using HTMX. It includes a script
tag, which adds the HTMX library for enabling dynamic updates without full-page reloads.
list.html
<ul>
{% for todo in todos %}
<li>
{{ todo.title }} - {{ todo.content }} |
<a href="" hx-get="{% url 'app_htmx:delete' todo.id %}" hx-target="#todos" >Delete</a>
</li> {% endfor %}
</ul>
This Django template snippet generates a list of TODOs dynamically. Each TODO is displayed with its title
and content
, along with a "Delete" link. The delete link uses HTMX to send a request to the delete
view and updates the #todos
element without reloading the page.
If not, you can get a copy of the source code here.
index.html
This post is for subscribers on our tiers: Developer Tier
To continue reading this article, upgrade your account to get full access.
Subscribe NowAlready have an account? Sign In