Proposal: Redefine specific {% block %} in an intermediate template

732 views
Skip to first unread message

Emil Stenström

unread,
Apr 17, 2013, 11:50:08 AM4/17/13
to django-d...@googlegroups.com
Hi,

Proposal:
Make it possible to use the same template block name twice, if the second one is nested within the first one. Inheriting from the template fills in the innermost block.

----------------------
Background (why is this useful?):
Say you have one base.html template defining a {% block content %}, and ten templates inheriting from it and defining the contents of the block. Now you decide that five of those templates actually should have a warning message at the top, and you decide to make a new base_with_warning.html template. You should should now be able to just change what block five of the templates inherit from, add the warning to your new base template, and be done with it?

Not really. Your base_with_warning.html would have to look like this:

{% extends "base.html" %}
{% block content %}
    <div class="warning">Be careful when changing these settings!</div>
    {% block content %}{% endblock %}
{% endblock %}

And this doesn't work in Django because duplicate block names are not allowed. I think making this possible would make cases like the one I'm describing above much easier to handle.

----------------------
Alternatives (this is how you should solve it instead...):
There are other ways to solve the problem I'm describing above. Let me list a few and talk about the problems I see with them:

1) Create a new template block in base_with_warning.html and change the five templates to use that block instead.

Problem: This puts the burden on developers to remember all kinds of content blocks they have in this project, and change them depending on what template they happen to be inherit from. The interface to the developer working on a new child template is much cleaner if they know they can always use the content block, and that the template they inherit from with handle the rest.

2) Make a child templates call super, and put the warning div inside the content block in base_with_warning.html.

Problem: It's very easy to forget calling super when making a new template. It quickly becomes repetitive to type block.super in each of the templates. Should I call super in the five templates without a warning too? Just to be sure it isn't forgotten if someone changes the inherited template.

3) Use an include tag in each of the child templates instead of inheriting.

Problem: This is just as repetitive as copy-pasting the div to each of the subtemplate, it's just the {% include "warning.html" %} is a shorter string to copy-paste. As with all copy-paste, it makes it easy miss one instance when changing the others. Not maintainable.

4) Use a template variable to decide whether to render the warning or not.

Problem: This moves the copy-paste to the view logic. Now I need to remember to pass the right variable in the view instead, with the same problem as with copy-paste above.

5) Write a custom template tag that does what I want.

Problem: I really don't know how to do this, and I haven't found someone that has written a tag like that after are plenty of googling.

----------------------
Backwards compatibility (this will not break stuff!):
Since duplicate block names are not allowed today, this change would be 100% backwards compatible. Everything would continue working as it always has.

----------------------
FAQ:
a) How would you inherit from a template with duplicate blocks with the same name? This proposal only deals with the case where the first block is defined inside the second block. If this happens the child templates content should fill the INNER block, not the outer one. There would be no way to override the outer block, since that would be shadowed by the redefined inner block.

b) If we allow for multiple blocks with the same name, what should happen with two blocks side by side (not nested)? We wouldn't allow that. This proposal is only about duplicate blocks when nested inside eachother. All other duplication would still yield the same error.

c) I have another suggestion that solves your problem without the problems with 1) - 5) above, that doesn't require a change in django? That really isn't a question, but fine :) Bring it on! I'm really interested in solving the problem described in the background section in a clean way, not a particular tech solution.

d) Do other people have this problem? I've found a few: http://stackoverflow.com/questions/15448211/django-template-block-overriding/ - http://stackoverflow.com/questions/8717521/django-multi-level-template-extends-and-nested-blocks - This could also be used to handle nicely breadcrumbs in multiple levels. You can probably think of more use-cases.

e) Is this the same as Ticket #4529: https://code.djangoproject.com/ticket/4529 ? No. The code in that ticket would still not work.

----------------------
Next steps:
I'm very eager to hear your thoughts on this, and maybe, if you think this is a good idea, start to write up a ticket.

----------------------
Thanks for reading this far! I love the work you all do on making Django better!

Andrew Ingram

unread,
Apr 17, 2013, 12:08:24 PM4/17/13
to django-d...@googlegroups.com
I've been wanting this exact feature for years. I've always struggled to explain the problem, but I've had numerous cases where this would have made for a vastly simpler template structure.

Big +1 from me.



--
You received this message because you are subscribed to the Google Groups "Django developers" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-develop...@googlegroups.com.
To post to this group, send email to django-d...@googlegroups.com.
Visit this group at http://groups.google.com/group/django-developers?hl=en.
For more options, visit https://groups.google.com/groups/opt_out.
 
 

Jacob Kaplan-Moss

unread,
Apr 17, 2013, 12:36:02 PM4/17/13
to django-developers
On Wed, Apr 17, 2013 at 10:50 AM, Emil Stenström <e...@kth.se> wrote:
> {% extends "base.html" %}
> {% block content %}
> <div class="warning">Be careful when changing these settings!</div>
> {% block content %}{% endblock %}
> {% endblock %}

I find this intensely confusing -- I can't build a mental model of
what's going to be rendered there. Allowing re-definining of existing
blocks nested inside those blocks... ouch. I'm -1 on allowing this; it
just seems incredibly confusing and error prone.

The general way I've solved this in the past is pretty straightforward::

{% block content-wrapper %}
<div class="warning">Be careful when changing these settings!</div>
{% block content %}{% endblock %}
{% endblock %}

What's wrong with this?

Jacob

Carl Meyer

unread,
Apr 17, 2013, 12:37:12 PM4/17/13
to django-d...@googlegroups.com
Hi Emil,

On 04/17/2013 09:50 AM, Emil Stenstr�m wrote:
> Proposal:
> Make it possible to use the same template block name twice, if the
> second one is nested within the first one. Inheriting from the template
> fills in the innermost block.

This is an interesting proposal, but I am concerned that the syntax you
propose is confusing on several levels:

1) If I haven't seen this done before, and I'm reading your template and
I see the two nested blocks, it's not at all intuitively obvious that
child templates would override the inner one rather than the outer one.
In fact, my naive assumption would have been the opposite.

2) If the "warning" HTML in your example is lengthy, I may see the outer
block and not immediately notice the inner one, and be confused as to
why the child templates aren't overriding the "content" block I can see
right in front of me.

I also think you missed the best (existing) solution to your example
problem:
Why not instead add a new block to base.html? So you'd change base.html
to have:

{% block outer-content %}
{% block content %}{% endblock content %}
{% endblock outer-content %}

And base_with_warning.html:

{% extends "base.html" %}

{% block outer-content %}
<div class="warning">...</div>
{% block content %}{% endblock content %}
{% endblock outer-content %}

I think this achieves your goal that none of the inheriting templates
have to change anything besides which template they extend. And it does
it with very little added verbosity in the base templates, a clearer
conceptual layering of blocks, and less potential for confusion when
reading the templates.

> 2) Make a child templates call super, and put the warning div inside the
> content block in base_with_warning.html.
>
> Problem: It's very easy to forget calling super when making a new
> template. It quickly becomes repetitive to type block.super in each of
> the templates. Should I call super in the five templates without a
> warning too? Just to be sure it isn't forgotten if someone changes the
> inherited template.
>
> 3) Use an include tag in each of the child templates instead of inheriting.
>
> Problem: This is just as repetitive as copy-pasting the div to each of
> the subtemplate, it's just the {% include "warning.html" %} is a shorter
> string to copy-paste. As with all copy-paste, it makes it easy miss one
> instance when changing the others. Not maintainable.
>
> 4) Use a template variable to decide whether to render the warning or not.
>
> Problem: This moves the copy-paste to the view logic. Now I need to
> remember to pass the right variable in the view instead, with the same
> problem as with copy-paste above.
>
> 5) Write a custom template tag that does what I want.
>
> Problem: I really don't know how to do this, and I haven't found someone
> that has written a tag like that after are plenty of googling.

I agree that none of these solutions are good.

Carl

Emil Stenström

unread,
Apr 17, 2013, 5:56:40 PM4/17/13
to django-d...@googlegroups.com
Jacob Kaplan-Moss skrev 2013-04-17 18:36:
> On Wed, Apr 17, 2013 at 10:50 AM, Emil Stenstr�m <e...@kth.se> wrote:
>> {% extends "base.html" %}
>> {% block content %}
>> <div class="warning">Be careful when changing these settings!</div>
>> {% block content %}{% endblock %}
>> {% endblock %}
>
> I find this intensely confusing -- I can't build a mental model of
> what's going to be rendered there. Allowing re-definining of existing
> blocks nested inside those blocks... ouch. I'm -1 on allowing this; it
> just seems incredibly confusing and error prone.

The mental model I have, and that I've seen many beginners have when
comming from other languages, is that the outmost tag is what you're
"filling in", and the innermost block is what you're making available
for child templates.

ASP.Net:
Parent: <asp:contentplaceholder id="Main" runat="server" />
Child: <asp:content ContentPlaceHolderID="Main" Runat="server"> ...
</asp:content>

JSF:
Parent: <ui:insert name="content" />
Child: <ui:define name="content"> ... </ui:define>

Rails:
Parent: <%= yield :head %>
Child: <% content_for :head do %> ... <% end %>

I'm not saying I think you should differetiate the two, just explaining
my mental model. The big difference is of course that I'm separating
what a template consumes and what it makes a block available to child
templates.

I understand the differences to your mental model, that nested blocks
are also available to child templates.

> The general way I've solved this in the past is pretty straightforward::
>
> {% block content-wrapper %}
> <div class="warning">Be careful when changing these settings!</div>
> {% block content %}{% endblock %}
> {% endblock %}
>
> What's wrong with this?

This is "Alternative 1" in my original message, sorry if this was
unclear. Say I change base.html to have a content-wrapper block, and put
your code in base_with_warning. Half my templates inherit from base and
half from base_with_warning. Now when I decide I want to add a warning
to a template that previously didn't have one, I change parent template
AND need to remember to rename the block to content instead of
content-wrapper. The "rename block"-part is the difference between our
two variants.

In projects with lots of templates, this quickly becomes a maintainance
nightmare. People forget the change the block name, and accidentially
don't get the warning they expect, even though they are explicitly
inheriting from base_with_warning. It's a mess.

This is the problem I'm trying to solve with my proposal. I agree it's
slightly backwards, but I couldn't come up with a smarter way.

/Emil

Emil Stenström

unread,
Apr 17, 2013, 6:00:29 PM4/17/13
to django-d...@googlegroups.com
Carl Meyer skrev 2013-04-17 18:37:
> Why not instead add a new block to base.html? So you'd change base.html
> to have:
>
> {% block outer-content %}
> {% block content %}{% endblock content %}
> {% endblock outer-content %}
>
> And base_with_warning.html:
>
> {% extends "base.html" %}
>
> {% block outer-content %}
> <div class="warning">...</div>
> {% block content %}{% endblock content %}
> {% endblock outer-content %}

Thanks for your reply! This is the same suggestion Jacob suggested one
minute before you. See my reply to him for my explanations of the
problems with this solution.

/Emil

Emil Stenström

unread,
Apr 17, 2013, 6:03:37 PM4/17/13
to django-d...@googlegroups.com
Andrew Ingram skrev 2013-04-17 18:08:
> I've been wanting this exact feature for years. I've always struggled to
> explain the problem, but I've had numerous cases where this would have
> made for a vastly simpler template structure.
>
> Big +1 from me.

Since there are a couple of -1:s on this. Would you mind sharing an
example? Preferably one where the alternatives wouldn't work.

/Emil

Carl Meyer

unread,
Apr 17, 2013, 6:09:26 PM4/17/13
to django-d...@googlegroups.com
Hi Emil,
I think you may need to re-read my suggestion more carefully.

This is not the same as your Alternative 1, and your objection does not
apply. Templates inheriting from either base.html or
base_with_warning.html can both override the "content" block, and don't
need to know or care about the "outer-content" block.

Carl

Emil Stenström

unread,
Apr 17, 2013, 6:28:45 PM4/17/13
to django-d...@googlegroups.com
Ah, I see what you mean now. Sorry for being slow, it's late here. It IS
different from Jacobs suggestion too.

It looks a little bit hacky to double-define the block in base.html.
There's the chance that some child templates to base.html start
inheriting from content, and some from outer-content, since both work.

Also this doesn't work if we want to do the same with base_with_warning,
as we then would have to add another wrapper block around content there.

Despite this problems, I agree that that's a nicer solution than all the
other alternatives.

/Emil

Emil Stenström

unread,
May 16, 2013, 5:21:05 PM5/16/13
to django-d...@googlegroups.com
Any feedback on how I was thinking? Does it make sense?

Based on the feedback so far I gather that changing the block tag is a bad idea. I'd love to continue working on this, because I've felt this need in lots of different projects. A new proposal could be in terms of a new pair of tags, maybe "layout" and "content". But before going further I would love some thoughts on the area, and if you think it's a worthwhile problem to solve.

/Emil


On Wednesday, 17 April 2013 23:56:40 UTC+2, Emil Stenström wrote:
Jacob Kaplan-Moss skrev 2013-04-17 18:36:

Carl Meyer

unread,
May 17, 2013, 11:03:29 AM5/17/13
to django-d...@googlegroups.com
Hi Emil,

On May 16, 2013, at 5:21 PM, Emil Stenström <e...@kth.se> wrote:
> Any feedback on how I was thinking? Does it make sense?
>
> Based on the feedback so far I gather that changing the block tag is a bad idea. I'd love to continue working on this, because I've felt this need in lots of different projects. A new proposal could be in terms of a new pair of tags, maybe "layout" and "content". But before going further I would love some thoughts on the area, and if you think it's a worthwhile problem to solve.

My feeling is that the existing tools are adequate, using wrapper blocks as I outlined earlier in the thread, so I don't see this as a problem that needs solving in the core template language.

If you're thinking of using new tag names anyway, you could certainly experiment with options in a third-party project and bring it back for consideration in the future if it's demonstrated usefulness and wide use.

Carl

Emil Stenström

unread,
May 18, 2013, 5:28:12 AM5/18/13
to django-d...@googlegroups.com
Carl Meyer skrev 2013-05-17 5:03 PM:
> Hi Emil,
>
> On May 16, 2013, at 5:21 PM, Emil Stenstr�m <e...@kth.se> wrote:
>> Any feedback on how I was thinking? Does it make sense?
>>
>> Based on the feedback so far I gather that changing the block tag
>> is a bad idea. I'd love to continue working on this, because I've
>> felt this need in lots of different projects. A new proposal could
>> be in terms of a new pair of tags, maybe "layout" and "content".
>> But before going further I would love some thoughts on the area,
>> and if you think it's a worthwhile problem to solve.
>
> My feeling is that the existing tools are adequate, using wrapper
> blocks as I outlined earlier in the thread, so I don't see this as a
> problem that needs solving in the core template language.

I agree that a wrapper is an OK workaround, but I think it feels ugly, a
little bit like adding a wrapper div to do something in CSS.

> If you're thinking of using new tag names anyway, you could certainly
> experiment with options in a third-party project and bring it back
> for consideration in the future if it's demonstrated usefulness and
> wide use.

Agreed. Since changing the block tag is probably out of question I'll
play with this as an external package instead.

Thanks for listening in giving me feedback!

/Emil
Reply all
Reply to author
Forward
0 new messages