In contrast to the first example, sometimes it’s most convenient to use a single view for both the full and the htmx view when using partials. A common example is paginated views.
Here, we typically want to display “more items”, loading more down the page, or we might want to display “next page”, swapping out the existing set of items. In this example I’m doing the first. This means that the paging control block gets replaced by the next page plus the next paging control.
The HTML looks like this:
Main template:
{% extends "base.html" %}
{% block body %}
<h1>List of monsters</h1>
{% if page_obj.paginator.count == 0 %}
<p>We have no monsters!</p>
{% else %}
{% include "_page_and_paging_controls.html" %}
{% endif %}
{% endblock %}
_page_and_paging_controls.html
partial:
{% for monster in page_obj %}
<p class="card">{{ monster.name }}</p>
{% endfor %}
{% if page_obj.has_next %}
<p id="paging-area">
<a href="#"
hx-get="?page={{ page_obj.next_page_number }}"
hx-target="#paging-area"
hx-swap="outerHTML"
>Load more</a>
</p>
{% else %}
<p>That's all of them!</p>
{% endif %}
</div>
Our single view function looks like this:
def paging_with_separate_partials(request):
if request.headers.get("Hx-Request", False):
template_name = "_page_and_paging_controls.html"
else:
template_name = "paging_with_separate_partials.html"
return TemplateResponse(
request,
template_name,
{
"page_obj": get_page_by_request(request, Monster.objects.all()),
},
)
However, this pattern comes up a lot, and TemplateResponse
has a very nice
feature: you can change the template that is used before the template actually
gets rendered.
This means we can move the if
statements into a for_htmx
decorator that
wraps up the logic for us - see implementation – so it looks quite a lot neater like this:
@for_htmx(use_template="_page_and_paging_controls.html")
def paging_with_separate_partials_improved(request):
return TemplateResponse(
request,
"paging_with_separate_partials.html",
{
"page_obj": get_page_by_request(request, Monster.objects.all()),
},
)
This decorator has some other tricks — it can match on specific Hx-Target
headers to choose different templates, which can be useful if you are doing more
advanced things, like both htmx search and paging in the same view.
Example view code, main template, partial template
Improvements to this pattern include:
Variants on this pattern include:
- Single view with actions combined (where we also include processing of POST in the single view, as well as multiple templates).
The same potential security issues apply here as described for inline partials.