[Django] #37038: {% extends %}/{% block %} doesn't work as expected with {% partialdef %}/{% partial %}

4 views
Skip to first unread message

Django

unread,
Apr 16, 2026, 9:13:00 AM (21 hours ago) Apr 16
to django-...@googlegroups.com
#37038: {% extends %}/{% block %} doesn't work as expected with {% partialdef %}/{%
partial %}
-------------------------------------+-------------------------------------
Reporter: Christophe Henry | Type: Bug
Status: new | Component: Template
| system
Version: 6.0 | Severity: Normal
Keywords: | Triage Stage:
| Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
First, please excuse me if this edge-case is actually documented or if
there's already an issue documenting it.

I recently had the use case where I needed to duplicate a block of buttons
present a the top of a form, to the bottom of it, on a template that was
extended by another to add a few extra buttons.

I wanted to use the newly added `{% partialdef %}` to solve the problem
but realised `{% partialdef %}` and `{% block %}`. Take the flowing
example:

{{{

{% partialdef btns inline %}
{% block btns_block %}
<button type="button">Save</button>
<button type="button">Publish</button>
{% endblock %}
{% endpartialdef %}

<!-- later -->
{% partial btns %}

}}}

And second template extending the previous:

{{{

{% block btns_block %}
<button type="button">Cancel</button>
<button type="button">Save</button>
<button type="button">Publish</button>
{% endblock %}

}}}

In this situation, the partial will be correctly overriden during the
first usage but not during the second. In the extending template, the
cancel button is present at the top of the page but not at the button.

If `{% partialdef %}` is not inline, the cancel button is not even there
at the top of the extending template.

I created a minimal reproducing example to illustrate this problem:
https://github.com/christophehenry/extends-partials-bug.
--
Ticket URL: <https://code.djangoproject.com/ticket/37038>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

Django

unread,
Apr 16, 2026, 10:53:18 AM (20 hours ago) Apr 16
to django-...@googlegroups.com
#37038: {% extends %}/{% block %} doesn't work as expected with {% partialdef %}/{%
partial %}
----------------------------------+--------------------------------------
Reporter: Christophe Henry | Owner: (none)
Type: Bug | Status: closed
Component: Template system | Version: 6.0
Severity: Normal | Resolution: wontfix
Keywords: | Triage Stage: Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
----------------------------------+--------------------------------------
Changes (by Natalia Bidart):

* cc: Carlton Gibson, Farhan Ali (added)
* resolution: => wontfix
* status: new => closed

Comment:

Hello Christophe Henry, thanks for the report!

After carefully reviewing the template engine internals, I will close this
ticket as `wontfix` as a design limitation rather than a bug. `{%
partialdef %}`/`{% partial %}` and `{% block %}`/`{% extends %}` are two
distinct, independent features of the template system. Partials create an
isolated rendering context by design (just like `{% include %}`) to keep
them portable and predictable. Block inheritance resolution lives in that
rendering context, so it is intentionally not visible when a partial is
rendered via `{% partial %}`.

The fact that the inline partial's first render in your example project
happens to see the block context is an implementation side effect, not a
design guarantee, and relying on it would be fragile. For the use case you
describe ("rendering the same button section at the top and bottom of a
form, with child templates able to customize it") the idiomatic Django
approach is to extract the shared markup into a helper template and use
`{% include %}` inside two distinct blocks. If the button markup is
simple, overriding both blocks inline is sufficient. If the button row is
more complex and you want to avoid duplicating the override, you can
define the extra content once using {% partialdef %} in the child template
and reference it via the `template_name#partial` syntax:

{{{#!django

{# _action_btns.html #}
<section>
<button type="button">Save</button>
<button type="button">Submit</button>
{% if extra_btns_template %}{% include extra_btns_template %}{% endif %}
</section>

{# base.html #}
{% block action_btns_top %}
{% include "_action_btns.html" %}
{% endblock %}

<p>Form content here.</p>

{% block action_btns_bottom %}
{% include "_action_btns.html" %}
{% endblock %}

{# extended.html #}
{% extends "base.html" %}

{% partialdef extra-buttons %}
<button type="button">Cancel</button>
{% endpartialdef %}

{% block action_btns_top %}
{% include "_action_btns.html" with extra_btns_template="extended.html
#extra-buttons" %}
{% endblock %}

{% block action_btns_bottom %}
{% include "_action_btns.html" with extra_btns_template="extended.html
#extra-buttons" %}
{% endblock %}
}}}

The default button markup (and potentially its "row" display) lives in one
place (`_action_btns.html`). The two blocks add a single line of
duplication in `base.html`, but that cost is low and the intent is
explicit. If the child's override is also long or reused across multiple
templates, it can be extracted into its own helper template or, as shown,
defined with `{% partialdef %}` directly in the child.

I'm cc'ing Carlton and Farhan for their thoughts, we may consider re-
opening to add a note to the `{% partialdef %}` docs clarifying that `{%
block %}` tags inside a partial are not connected to the template
inheritance chain.
--
Ticket URL: <https://code.djangoproject.com/ticket/37038#comment:1>

Django

unread,
Apr 16, 2026, 4:53:44 PM (13 hours ago) Apr 16
to django-...@googlegroups.com
#37038: {% extends %}/{% block %} doesn't work as expected with {% partialdef %}/{%
partial %}
----------------------------------+--------------------------------------
Reporter: Christophe Henry | Owner: (none)
Type: Bug | Status: closed
Component: Template system | Version: 6.0
Severity: Normal | Resolution: wontfix
Keywords: | Triage Stage: Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
----------------------------------+--------------------------------------
Comment (by Carlton Gibson):

Yes, partials are (by design) local to the template defining them, and not
intended to work with extends or blocks.

Happy to review a docs clarification for this, but (beyond emphasising
that partials apply to the defining template) I wouldn’t want to labour
the point. (In the whole lifetime of templates-partials this kind of point
has come up rarely, and the explanation may muddy the water more than
clear it.)
--
Ticket URL: <https://code.djangoproject.com/ticket/37038#comment:2>
Reply all
Reply to author
Forward
0 new messages