Opened 5 years ago

Closed 5 years ago

Last modified 5 years ago

#13399 closed (duplicate)

included template can't update blocks

Reported by: lanyjie Owned by: nobody
Component: Template system Version: 1.1
Severity: Keywords: include ssi extends interplay
Cc: Triage Stage: Unreviewed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: UI/UX:


I encountered this problem in django 1.1, and it caught me in a surprise. This is a short version of the situation, I have four files:

{% block title %} Base {% endblock %}

{% extends "base.html" %}
{% if use_branch_one %}
{% include "branch1.html" %}
{% else %}
{% include "branch2.html" %}
{% endif %}

{% block title %} This is branch one {% endblock %}

{% block title %} This is branch two {% endblock %}

I would expect to see the main.html having either of the two titles in the branches, but what happens is you always see the base title!

The interpretation of inclusion should be clear: the included file is first read into the place holder "AS IS", then template processing continues from that very point, within the current context of the including template, as if nothing special happened -- and of course that context also specifies the parent template. With that understanding, we should be surprised to see that a block clause in the included template file becomes non-effective. I think the documentation about these builtin tags (include, and similarly ssi) leaves some ambiguity. It could be dynamically included (as you can give use a variable argument for it, so what file to be included is definitely determined at runtime; another situation is to have the include clause inside some conditional clause), but what we said before still applies in this dynamical loading context. If the inheritance is lost in the included file, we are definitely having a lesser template system. Currently what you can do is to factor out the {% block title %} clause in the branches html files into the main.html file. But it wont' work if you have multiple blocks to update in your included template file.

I noticed there are other somehow related tickets about the problems occur at the interplay of "include/ssi" and "extends" (see ticket 6646 and 2908), and they all stems from the same ambiguity of what shall happen with "include/ssi". With this understanding, ticket 2908 could be resolved by allowing multiple definition of the same block (whether included or not), but only the last definition is taken (or, the one comes after overrides the one goes before). Ticket 6646 can be resolved by specifying that the parent/extended template must be first parsed to determine the included blocks that will be available to the child/extending template to fill in.

Change History (7)

comment:1 follow-up: Changed 5 years ago by lanyjie

  • Needs documentation unset
  • Needs tests unset
  • Patch needs improvement unset

The case presented in ticket 6646 can be even treated as a static include: as if the included files are copy&pasted into the place holder, with those conditional clause untouched. One really dynamic example is this: {% include file_name_var %}, where file_name_var is a variable to be specified in the context. To implement this feature, one possible way is this:

  1. a context that collects the block definitions on the fly shall first run through the base template, and upon a include clause, shall run into the included file.
  1. the child template is then run through, block definitions are updated on the fly, even when following into included templates.
  1. and finally the parent at root is filled up and the output is generated.

comment:2 in reply to: ↑ 1 ; follow-up: Changed 5 years ago by lanyjie

Sorry, the above change is only meant to be additional comments, really not a change after all. Since multiple inheritance is not allowed, there is always a single line of inheritance from the child to the root. For example:

C extends B
B extends A (and A is the root)

Then the inheritance chain is: C -> B -> A.

If we allow multiple definition of a block, but the later one overrides the contents of the one before it (in such a fashion, the first definition is the place holder, but only the last definition determines the final content to be filled into the place holder; also notice the block.super is now interpreted as the contents of the one immediately before the current definition -- and this is a natural extension), then inheritance can be actually "implemented" by inclusions. Here is what I mean: simply substitute every {% extends "..." %} with an {% include "..." %}. Since these inclusions are static, as {% extends "..." %} are always static, we are effectually concatenating all the file on the inheritance chain! And now you have a file C' like this:

C' = A + B + C

Then you simply work through C', and you should get the same final results (ignoring all the 'extends' therein).

comment:3 in reply to: ↑ 2 Changed 5 years ago by lanyjie

Replying to lanyjie:
The above specification of include/extends offers the following advantages:

  1. resolves the issues in the aforementioned tickets -- no more interplay between include and extends: as they are now the same in effect.
  1. compatible with previous specifications, and there is little room for ambiguities.
  1. if the inheritance chain is very long, it is possible to concatenate the files in the chain at compilation, which could improve performance.
  1. it is possible to define new blocks in the child in two different ways:

(a) define a new block INSIDE a block already defined.

(b) if the parent didn't close the <body> tag, a top level block can be defined in the child.

comment:4 follow-ups: Changed 5 years ago by russellm

  • Resolution set to duplicate
  • Status changed from new to closed

Closing as a duplicate of #12008; although that is reporting a slightly different problem, the end resolution for the ticket is that there is a need to improve the documentation explaining how blocks and includes work in templates.

The short explanation is that {% include %} doesn't participate in block import structure. Blocks are evaluated, then includes, then the remainder of template nodes.

comment:5 in reply to: ↑ 4 Changed 5 years ago by lanyjie

  • Resolution duplicate deleted
  • Status changed from closed to reopened

Replying to russellm:

That explains why those unexpected/undesirable things happen. Though current code is correct in its own sense, but the issues remains. If you maybe give my proposal some thoughts, maybe we could really resolve those issues.

comment:6 Changed 5 years ago by russellm

  • Resolution set to duplicate
  • Status changed from reopened to closed

Please don't reopen a ticket that was closed by a core developer. The block/include interaction works as designed. If you want to discuss potential changes, please start a thread on django-developer -- but please be sensitive to the fact that we're in the final days of a release cycle, so discussing major changes to the template language isn't a high priority right now. Once the release is out the door, it will be easier to get developer attention.

comment:7 in reply to: ↑ 4 Changed 5 years ago by lanyjie

Replying to russellm:

... Blocks are evaluated, then includes, then the remainder of template nodes.

What about this: first includes, then blocks, then everything else. This should resolve all those issues above. (Thanks for the kind suggestion, I won't reopen it, but hopefully you would still catch this comment)

Note: See TracTickets for help on using tickets.
Back to Top