#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>