Feature proposal: Conditional block tags

312 views
Skip to first unread message

Markus Amalthea Magnuson

unread,
Jan 30, 2015, 1:46:52 PM1/30/15
to django-d...@googlegroups.com
Hey all,

Tim Graham suggested I posted to this list regarding an idea for a new feature: conditional block tags.

I've summarized the feature in this ticket: https://code.djangoproject.com/ticket/24232

The basic idea is to be able to write something like this in a template:

{% block myblock if myvariable %}my content{% endblock %}

If the condition is false it is as if the block wasn't there, meaning its parent would be rendered instead, if it exists.

This is really a short form of:

{% if myvariable %}
  my content
{% else %}
  {{ block.super }}
{% endif %}

Note that it may seem similar to ticket #9173 but they are different, which I try to make clear in this comment: https://code.djangoproject.com/ticket/24232#comment:2

So, what do you think?

Aymeric Augustin

unread,
Jan 31, 2015, 7:43:42 AM1/31/15
to django-d...@googlegroups.com
Hello,

That’s a rather specialized behavior for the general purpose {% block %} tag.

I’m not convinced that building in such specialized behavior beats composing blocks i.e. handling the condition with a {% if %} tag.

The Django template language has way too much ad-hoc, inconsistent syntax in its built-in tags. I don’t like the idea of adding more.

We have to balance saving keystrokes against increasing the amount of things every Django user needs to know. In this regard, adding that new syntax looks like a bad tradeoff to me.

-- 
Aymeric.



--
You received this message because you are subscribed to the Google Groups "Django developers (Contributions to Django itself)" 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.
To view this discussion on the web visit https://groups.google.com/d/msgid/django-developers/9ce259ff-6a2e-493a-a6df-1c15b43fe9c0%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Marc Tamlyn

unread,
Jan 31, 2015, 8:17:40 AM1/31/15
to django-d...@googlegroups.com
I personally don't see any problem with the existing syntax and find it much easier to understand.

Markus Amalthea Magnuson

unread,
Jan 31, 2015, 11:42:25 AM1/31/15
to django-d...@googlegroups.com
I see both of your points, but I should also mention that a better example than what I provided is when you want to have a block inside an if-statement, which is not possible today:

{% if myvariable %}
  {% block myblock %}my content{% endblock %}
{% endif %}

That inner block will always be rendered. A conditional version would look like:

{% block myblock if myvariable %}my content{% endblock %}

Anyway, if there is still consensus that this is not a desirable feature, feel free to close the ticket at: https://code.djangoproject.com/ticket/24232

Unai Zalakain

unread,
Jan 31, 2015, 1:11:03 PM1/31/15
to django-d...@googlegroups.com
Hi!

>I see both of your points, but I should also mention that a better
>example than what I provided is when you want to have a block inside an
>if-statement, which is not possible today:
>{% if myvariable %}
> {% block myblock %}my content{% endblock %}
>{% endif %}

Correct me if I'm wrong but the same exact behaviour can be achieved by:

{% block myblock %}
{% if myvariable %}
my content
{% else %}
{{ block.super }}
{% endif %}
{% endblock myblock %}

--
unai
signature.asc

Raphael Michel

unread,
Jan 31, 2015, 4:12:44 PM1/31/15
to Unai Zalakain, django-d...@googlegroups.com
Hi,

Am Sat, 31 Jan 2015 18:12:57 +0000
schrieb Unai Zalakain <un...@gisa-elkartea.org>:
> Correct me if I'm wrong but the same exact behaviour can be achieved
> by:
>
> {% block myblock %}
> {% if myvariable %}
> my content
> {% else %}
> {{ block.super }}
> {% endif %}
> {% endblock myblock %}

I guess you're right, but that's exactly Markus' point. This is
inconvenient -- it is exactly one of those tradeoffs Aymeric
mentioned, and seems not well balanced to me, as does not look good in
the template code and I don't suppose that every Django developers
knows about {{ block.super }}.

What is the technical reason for {% if … %}{% block %} not being
possible (or, not behaving as expected)? Enabling this syntax would
improve readability a lot and satisfy Markus' request as well
without introducing new syntax.
(Although from my uneducated guesses of how the blocks work, this might
be very hard and not worthy to implement.)

Raphael

Unai Zalakain

unread,
Jan 31, 2015, 4:34:01 PM1/31/15
to Raphael Michel, django-d...@googlegroups.com
Hi,

>I guess you're right, but that's exactly Markus' point. This is
>inconvenient -- it is exactly one of those tradeoffs Aymeric
>mentioned, and seems not well balanced to me, as does not look good in
>the template code and I don't suppose that every Django developers
>knows about {{ block.super }}.

Introducing yet more syntax ({% block … if … %}) doesn't seem like a
solution to me.

>What is the technical reason for {% if … %}{% block %} not being
>possible (or, not behaving as expected)? Enabling this syntax would
>improve readability a lot and satisfy Markus' request as well
>without introducing new syntax.
>(Although from my uneducated guesses of how the blocks work, this might
>be very hard and not worthy to implement.)

{% if … %}{% block … %} seems indeed the most natural way a user would
try to achieve this behaviour. In fact, I remember my surprise a couple
of years ago when I discovered it didn't work.


--
unai
signature.asc

Aymeric Augustin

unread,
Jan 31, 2015, 4:36:29 PM1/31/15
to django-d...@googlegroups.com
On 31 janv. 2015, at 22:12, Raphael Michel <ma...@raphaelmichel.de> wrote:
> What is the technical reason for {% if … %}{% block %} not being
> possible (or, not behaving as expected)?

I suspect it has to do with {% if %} being interpreted at runtime while
{% block %} is interpreted at compile time.

I never investigated this fully. If someone does, I’m interested :-)

--
Aymeric.




Raphael Michel

unread,
Feb 1, 2015, 8:57:44 AM2/1/15
to Aymeric Augustin, django-d...@googlegroups.com
Hi,

Am Sat, 31 Jan 2015 22:36:05 +0100
schrieb Aymeric Augustin <aymeric....@polytechnique.org>:
> I suspect it has to do with {% if %} being interpreted at runtime
> while {% block %} is interpreted at compile time.
>
> I never investigated this fully. If someone does, I’m interested :-)

This was my guess as well. However, if this indeed is the reason, then
a {% block … if … %} syntax would make it impossible to interpret
blocks at compile time, and I don't think Markus' proposal (or the {%
if %}{% block %} syntax, which I do like more than {% block if %}) is
worth that effort.

Raphael

Markus Amalthea Magnuson

unread,
Feb 1, 2015, 10:33:09 AM2/1/15
to django-d...@googlegroups.com, Aymeric Augustin
Which is, I guess, why it is the block tag's behavior that would need to change rather than the if tag's, if support for blocks inside if tags were to be implemented.

Have not had a look myself at this though, but I too was confused the first few times I tried to put blocks in if statements and not getting it to work.

--
You received this message because you are subscribed to the Google Groups "Django developers  (Contributions to Django itself)" 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.

Collin Anderson

unread,
Feb 1, 2015, 10:34:52 AM2/1/15
to django-d...@googlegroups.com
Sounds like a spot where we might be able to improve the documentation.

Preston Timmons

unread,
Feb 2, 2015, 12:49:45 PM2/2/15
to django-d...@googlegroups.com
I suspect it has to do with {% if %} being interpreted at runtime while
{% block %} is interpreted at compile time.

I never investigated this fully. If someone does, I’m interested :-)

--
Aymeric.

I took a look at how this works. The issue comes from a naive approach to how block nodes are selected to be overridden in the ExtendsNode. I wrote some pseudocode below that I think clarifies what's happening:

Compile step:

parser.parse() is called
-  if got extends node
  - call do_extends
    - call parser.parse() to get rest of nodelist for current template *
    - ExtendsNode.__init__
      - get block nodes with nodelist.get_nodes_by_type(BlockNode) **
    - ExtendsNode.render
      - add BlockContext instance to context ***
      - add block nodes from __init__ call to BlockContext
      - compile parent
      - add parent blocks to BlockContext **
      - call parent._render(context)

* At this point, it's possible nodelist has IfNodes that contain BlockNodes.
** nodelist.get_nodes_by_type(BlockNode) returns all block nodes, ignoring any conditionals
*** BlockContext is a defaultdict of block name -> block list, where block_context[name][-1] is the winning block.

Render step:

When BlockNodes are rendered they check for a BlockContext in the context. If an entry exists in BlockContext with the same name, it is rendered in place of the current block. Because block_context[name][-1] is the one that wins, and blocks are added to BlockContext regardless of the condition around it, the parent block is always overridden.

Preston Timmons

unread,
Feb 17, 2015, 3:04:38 PM2/17/15
to django-d...@googlegroups.com
After looking at this, there's nothing special about blocks compared to any other node. They could just as well be evaluated at render time.

Here's a branch that implements this change:


Before returning blocks into block_context to be used as overrides, it loops through any if nodes and checks the conditions.

The one case that's kinda funny is if you do something like:

{% if var %}
    {% block content %}...{% endblock %}
{% else %}
    {% block content %}...{% endblock %}
{% endif %}

Currently, the second block node will raise a TemplateSyntaxError because it's defined twice. Does that matter?

Tino de Bruijn

unread,
Feb 18, 2015, 5:30:00 AM2/18/15
to django-d...@googlegroups.com
Hi,

I don't think it does. The construct is useless anyway. The only use case I could think of is:

{% if var %}
    <div>var specific content</div>
    {% block content %}...{% endblock %}
{% else %}
    {% block content %}...{% endblock %}
{% endif %}

But that could also be created with the block outside the if statement. You would probably use it in the sense of "only include this block if", or "if a, block, else block b". Redefining the content block does not seem to make sense to me in those cases.

Tino
Reply all
Reply to author
Forward
0 new messages