Proposal: {% transclude %} - {% include %} with super powers

I propose a {% transclude %} template tag.

{% transclude %} is equivalent to {% include %}, except that it also lets you define blocks of content to be passed as template variables to the “transcluded” template.

The name comes from Angular, which uses the term in a similar way (note - I’ve only dabbled with Angular, so I’m no expert).

Example

Consider the following base template (base.html):

<html>
    <head>
        <title>{{ title }}</title>
    </head>
    <body>
        <aside>{{ sidebar_content }}</aside>
        <main>{{ content }}</main>
    </body>
</html>

Then this template:

{% transclude 'base.html' with title="Hello, World!" %}
{% transclude_var sidebar_content %}
            <nav><a href="/">Home</a></nav>
{% transclude_var content %}
            <p>Hello, World!</p>
{% endtransclude %}

Will Produce:

<html>
    <head>
        <title>Hello, World!</title>
    </head>
    <body>
        <aside>
            <nav><a href="/">Home</a></nav>
        </aside>
        <main>
            <p>Hello, World!</p>
        </main>
    </body>
</html>

New Possibilities

{% transclude %} allows you to easily create “wrapper” or “layout” templates, which would otherwise require custom template tags. Ie. expand_widget.html:

<details class="expand-widget">
    <summary>{{ summary }}</summary>
    <div class="expand-widget-content">
        {{ content }}
    </div>
</details>

This is a pattern that I’ve always found Django sorely lacking.

Replacing {% extends %}

One can easily convert “base templates” (intended to be used with {% extends %}) for use with {% transclude %}. Ie:

<html>
    <head>
        <title>{{ title }}</title>
    </head>
    <body>
        <aside>{% block sidebar_content %}{% endblock %}</aside>
        <main>{% block content %}Default Content{% endblock %}</main>
    </body>
</html>

Becomes:

<html>
    <head>
        <title>{{ title }}</title>
    </head>
    <body>
        <aside>{{ sidebar_content }}</aside>
        <main>{% if content %}{{ content }}{% else %}Default Content{% endif %}</main>
    </body>
</html>

This approach is conceptually simpler (we’ve unified the concepts of {{ content }} and {% block content %}). It’s also more flexible. The base template can now be transcluded by another template, or rendered directly (via TemplateResponse, render_to_string, etc.) with sidebar_content and content being generated by any means you desire.

Next Steps

I’d be happy to take a stab at implementing this myself, and maybe producing some more meaningful examples. But first I’d like to see if there is any wider interest in this idea. Has anything like this been discussed before? I tried a quick search both here and in the issue tracker, and couldn’t find anything relevant.

1 Like