Is it possible in Jinja2 to have this behaviour? - templates

I have a list of strings that I will process and for brevity sake I am writing them as spans here.
I want some html to go inside a div, so,
<div class="something">
<span>something1</span>
<span>something2</span>
<span>something3</span>
...
</div>
And some other outside (depending on some condition)
<span>something-else1</span>
<span>something-else2</span>
<span>something-else3</span>
...
So the condition comes from something in the list. Is there any way so I can stop the control at a macro caller so I can have the same macro be called again so it would append the next span in the same place?
So something like
{% macro render_inside_div %}
<div class="something">
{{ caller() }} <-- I want to render multiple spans in within the same div
</div>
{% endmacro %}
EDIT:
Yes, of course that would work and that is (sort of) how I got it to work anyways. But I was looking for a 'cleaner' and DRY solution. So basically I would just iterate the list once and the way it would work would be something like this:
{% for row in my_list %}
{% if row.condition_for_div %}
{% call render_in_div %} {# Look before to see the macro I've written above #}
{{ do_something(row.data) }}
{% endcall %}
{% else %}
{{ do_something(row.data) }}
{% endif %}
{% endfor %}
so in this render_in_div I want to hand control to the caller() block many times, so the control (for that "context") would remain there until I say to get out (in some way like a generator?)

Related

Update variables passed with django include tag

I have a for loop that contains an include tag like the following:
{% for thing in things %}
{% block example %}
{% include 'myapp/example.html' with thing=thing %}
{% endblock %}
{% endfor %}
I expected that the html in example.html would get rendered with each thing within things but it only gets rendered with the first thing object. Is there a way to pass each of the thing objects within things to example.html?
Your code is supposed to work, even with the {% block %} tags, as long as you do not redefine them somewhere else. Maybe the problem is on the things or myapp/example.html side?
Note: if you refer to thing with the same identifier in your child template, you don't need the with thing=thing part - for exemple, {{ thing }} will be directly avalaible in your child template.
Something you might want to try:
{% for thing in things %}
{% include 'myapp/example.html' %}
{{ thing }}
{% endfor %}

Jinja2 templating with components? blocks? templates?

A little question to jinja2 templating:
I want to create a reusable template to include and then overwrite blocks. Macros do not let me write junks of HTML easily as parameters do they? Say I want to reuse an include several times and am using BIG junks of HTML in blocks that I want to dynamically assign
how would I do it?
certainly not with macros I guess, or am I wrong?
{% render_foo('bar',2) %} is fine
{% render_foo('<table><tr><th>something</th><th>somethingelse</th></tr><tbody><tr>....etc') %} is not fine any more is it
"what do you really want to do?"
yes, what I told you, I have a way I create containers for my data. The container is ALWAYS the same. The content is completely different on each usage. Once a table. Once a bootstrap component. Once a form.
The surrounding elements are always the same
to reproduce the simple error this is what I did:
{% include 'full_section.html' %}
{% block fullsection %} <table><tr><th>something</th><th>somethingelse</th></tr><tbody><tr>....etc{% endblock %}
{% include 'full_section.html' %}
{% block fullsection %} <form>//some cool LONG big form </form>{% endblock %}
full_section.html contents just for completeness, it is a lot more complex in reality
<div class="my_cool_full_section">
{% block full_section %}{% endblock %}
</div>
TemplateAssertionError: block 'fullsection' defined twice
I found the answer well hidden in the jinja2 docs
http://jinja.pocoo.org/docs/2.9/templates/#block-assignments
so you use a macro and a block assignment e.g. like this:
{% set section_content %}
<table><tr><td>etc</td> <td>etc</td> <td>etc</td></tr></table>
<table><tr><td>etc</td> <td>etc</td> <td>etc</td></tr></table>
<table><tr><td>etc</td> <td>etc</td> <td>etc</td></tr></table>
{% endset %}
{{ render_full_size_section(section_content) }}
{% set section_content %}
aaaaaaaaaaa
{% endset %}
{{ render_full_size_section(section_content) }}
wonder what they were doing pre 2.8... dark dark middle age
then in the macro:
{% macro render_full_size_section(content) %}
<div class="mycoolsection">
{{ content | safe }}
</div>
{% endmacro %}

using a loop result as a variable for child loop

I think the best way to describe is problem is with an example.
{% for content in contents %}
{% for stuff in {{content}} %}
{{stuff}}
{% endfor %}
{% endfor %}
I am using google app engine webapp templates. I can't seem to use a result from the parent forloop {{content}} as a variable for its child forloop. TemplateSyntaxError: Could not parse the remainder: '{{content}}' from '{{content}}' Is it possible to do this? Thanks!!
You can use only content without braces around:
{% for content in contents %}
{% for stuff in content %}
{{ stuff }}
{% endfor %}
{% endfor %}
When you are inside the first for-loop, content exists in the context, as any other variable. Same thing for stuff in the inner loop. Plus, blocks are generally using argument as variables, except in it is surrounded by quotes.
The {{ }} notation can be use to only display the variable in the document.

django template: how to enclose a variable

I would like to make a sequence of variables within a for loop such as name0, name1, .... How do I do that? Thanks.
{% for i in '1234567890' %}
{% if name{{forloop.counter0}} %}
...
{% endif %}
...
{{name{{forloop.counter0}}}}
...
{% endfor %}
is as simple as
{{ name }}{{ forloop.counter0 }}
for the if, you should use the "with" statement:
{% with name|add:forloop.counter0 as if_test %}
{% if if_test %}
... <!-- do whatever you need to do here -->
all this must be inside your for loop
As you can see, the Django templating language tries hard to keep you from doing what you're trying to do, encouraging you to do your data processing in your view code, instead of your templates. For your example, in your view code, you might try doing:
context['names'] = [name for name in names[:10]]
...instead of creating individual variables for each name.
Then in your template:
{% for name in names %}
{% if name %}
...
{% endif %}
...
{{name}}
...
{% endfor %}
As far as I can tell, that would have the same effect as your code, but you would be doing your aggregation of the names in the view, instead of the template. If I'm reading the intent of your code wrongly, please provide more context, but it doesn't seem like you're doing anything that requires template logic.

Alternate Row Coloring in Django Template with More Than One Set of Rows

Django templates offer the builtin tag cycle for alternating between several values at different points in a template (or for loop in a template) but this tag does not reset when it is accessed in a scope outside of the cycles definition. I.e., if you have two or more lists in your template, the rows of all of which you'd like to use some css definitions odd and even, the first row of a list will pick up where the last left off, not with a fresh iteration from the choices (odd and even)
E.g., in the following code, if the first blog has an odd number of entries, then the first entry in a second blog will start as even, when I want it to start at odd.
{% for blog in blogs %}
{% for entry in blog.entries %}
<div class="{% cycle 'odd' 'even' %}" id="{{entry.id}}">
{{entry.text}}
</div>
{% endfor %}
{% endfor %}
I've tried obviating this by patching with the resetcycle tag offered here:
Django ticket: Cycle tag should reset after it steps out of scope
to no avail. (The code didn't work for me.)
I've also tried moving my inner loop into a custom tag, but this also did not work, perhaps because the compile/render cycle moves the loop back into the outer loop? (Regardless of why, it didn't work for me.)
How can I accomplish this simple task!? I'd prefer not to create a data structure in my view with this information pre-compiled; that seems unnecessary. Thanks in advance.
The easiest workaround (until the resetcycle patch gets fixed up and applied) is to use the built-in "divisibleby" filter with forloop.counter:
{% for entry in blog.entries %}
<div class="{% if forloop.counter|divisibleby:2 %}even{% else %}odd{% endif %}" id="{{ entry.id }}">
{{ entry.text }}
</div>
{% endfor %}
A little more verbose, but not hard to understand and it works great.
https://docs.djangoproject.com/en/1.8/ref/templates/builtins/#cycle
{% for o in some_list %}
<tr class="{% cycle 'row1' 'row2' %}">
...
</tr>
{% endfor %}
Give up and use Jinja2 Template System
I gave up on django template language, it's very restricted in what you can do with it.
Jinja2 uses the same syntax that the django template uses, but adds many enhancements over it.
EDIT/NOTE ( I know it sounds like a big switch for just a minor issue, but in reality I bet you always find yourself fighting the default template system in django, so it really is worthwhile and I believe it will make you more productive in the long run. )
You can read this article written by its author, although it's technical, he mentions the problem of the {% cycle %} tag in django.
Jinja doesn't have a cycle tag, it has a cycle method on the loop:
{% for user in users %}
<li class="{{ loop.cycle('odd', 'even') }}">{{ user }}</li>
{% endfor %}
A major advantage of Jinja2 is that it allows you to use logic for the presentation, so if you have a list of pictures, you can put them in a table, because you can start a new row inside a table every N elements, see, you can do for example:
{% if loop.index is divisibleby(5) %}
</tr>
{% if not loop.last %}
<tr>
{% endif %}
{% endif %}
you can also use mathematical expressions:
{% if x > 10 %}
and you can access your python functions directly (but some setup is required to specify which functions should be exposed for the template)
{% for item in normal_python_function_that_returns_a_query_or_a_list() %}
even set variables ..
{% set variable_name = function_that_returns_an_object_or_something() %}
You can use tagged cycle and resetcycle (new in Django 1.11) calls (from https://docs.djangoproject.com/en/1.11/ref/templates/builtins/#std:templatetag-resetcycle ):
{% for blog in blogs %}
{% cycle 'odd' 'even' as rowcolors silent %}
{% resetcycle rowcolors %}
{% for entry in blog.entries %}
{% cycle rowcolors %}
<div class="{{ rowcolors }}" id="{{entry.id}}">
{{ entry.text }}
</div>
{% endfor %}
{% endfor %}
I end up doing so, with the forloop.counter0 - It works great!
{% for product in products %}
{% if forloop.counter0|divisibleby:4 %}<div class="clear"></div>{% endif %}
<div class="product {% if forloop.counter0|divisibleby:4 %}col{% else %}col20{% endif %}">
Lorem Ipsum is simply dummy text
</div>
{% endfor %}
The easiest answer might be: "give up and use jQuery." If that's acceptable it's probably easier than fighting with Django's templates over something so simple.
There's a way to do it server-side with an iterator that doesn't keep a simultaneous copy of all the entries:
import itertools
return render_to_response('template.html',
{
"flattened_entries": itertools.chain(*(blog.entries for blog in blogs)),
})