← Today I Learned

Conditionally extend a template

Django supports template inheritance via the extends tag. Often, a template inheritance chain follows all the way up to a “base” template that contains the full markup for a page, from the doctype to the closing HTML tag.

A common pattern when using libraries like htmx is returning HTML fragments. In these cases, it can make sense to conditionally extend a template: return the full page when visited directly but return a fragment when requested via Ajax.

Let’s say we have this page layout, layout.html:

<!DOCTYPE html>
<html>
  <head>
    <!-- ... -->
  </head>
  <body>
    {% block content %}{% endblock %}
  </body>
</html>

A template rendered within this layout would look something like this:

{% extends "layout.html"}

{% block content %}<p>Hello, world!</p>{% endblock %}

To render the template standalone, we can create a “stub” layout (say, ajax.html) that declares the block and nothing else:

{% block content %}{% endblock %}

Then, instead of using a string literal for the name of the template to extend, we use a variable. We can default to layout.html if it’s omitted:

{% extends layout|default:"layout.html"}

{% block content %}<p>Hello, world!</p>{% endblock %}

The view that renders the template might look something like this:

def view(request: HttpRequest):
  return render(
    request,
    template_name="some_template.html",
    context={ "layout": "ajax.html" if request.headers.get("hx-request") else None },
  )