Access parent for loop scope in child template in Jinja2 - python

parent.txt
{% for dict in list_of_dictionaries %}
{% block pick_dictionary_element %}
{% endblock %}
{% endfor %}
child_one.txt
{% extends "parent.txt" %}
{% block pick_dictionary_element %}
{{ dict.a }}
{% endblock %}
child_two.txt
{% extends "parent.txt" %}
{% block pick_dictionary_element %}
{{ dict.b }}
{% endblock %}
Then:
from jinja2 import Template, Environment, FileSystemLoader
e = Environment(loader=FileSystemLoader("./"))
e.get_template("child_one.txt").render(list_of_dictionaries=[{'a': 'a', 'b': 'b'}])
produces an empty output. How can I access the dict var from the parent for loop? I kind of imagined jinja just in-lining the pick_dictionary_element and the child having the for loop scope of its parent?

The key to what you are trying to do is to use the scoped keyword on your block:
{# parent.txt #}
{% for dict in list_of_dictionaries %}
{% block pick_dictionary_element scoped %}
{% endblock %}
{% endfor %}
Why was this hard to debug?
You made the mistake of using the name dict in the loop:
{% for dict in list_of_dictionaries %}
The side effect of this was that the child template did not readily complain, since the symbol dict exists in its context. If instead, you had done something like:
{# parent.txt #}
{% for a_dict in list_of_dictionaries %}
{% block pick_dictionary_element %}
{% endblock %}
{% endfor %}
{# child_one.txt #}
{% extends "parent.txt" %}
{% block pick_dictionary_element %}
{{ a_dict.a }}
{% endblock %}
You would have been told:
jinja2.exceptions.UndefinedError: 'a_dict' is undefined

Starting with Jinja 2.2, you can explicitly specify that variables are available in a block by setting the block to “scoped” by adding the scoped modifier to a block declaration
http://jinja.pocoo.org/docs/2.9/templates/#block-nesting-and-scope
{# parent.txt #}
{% for dict in list_of_dictionaries %}
{% block pick_dictionary_element scoped %}
{% endblock %}
{% endfor %}

Related

How to extend multiple bases or conditional {% extends %} in one html page in django?

{% if request.user.profile.emp_desi in qa_list %}
{% extends "qa_base.html" %}
{% elif request.user.profile.emp_desi in mgr_list %}
{% extends "manager_base.html" %}
{% else %}
{% extends "common_base.html" %}
{% endif %}
How can I solve this problem??
based on designation I want to extend different different bases.
You can use include
You need to create a directory and template layout in such a way that
you will have one base and multiple includes.
https://docs.djangoproject.com/en/4.0/ref/templates/builtins/#include
{% if request.user.profile.emp_desi in qa_list %}
{% include "qa_base.html" %}
{% elif request.user.profile.emp_desi in mgr_list %}
{% include "manager_base.html" %}
{% else %}
{% include "common_base.html" %}
{% endif %}

How can I check that variable is changed in jinja2 template?

I have the list of objects to show on web pages(HTML file).
And the type(graph, table etc.) of objects is different from the situation.
If there is a graph objects, I should load js and css files about a graph.
Because I do not want to load js, css files for graph when there is no graph object in the list,
I have implemented the following jinja2 template HTML file.
{% block body %}
{% set has_graph = 0 %}
{% for item in components %}
{% if item.form == 'graph' %}
{% set has_graph = 1 %}
{% endif %}
{% endfor %}
{% if has_graph == 1 %}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.6/d3.min.js"></script>
{% endif %}
{% endblock %}
I have found {% set has_graph = 1 %} worked, but the js file was not loaded.
I do not know why {% if has_graph == 1 %} does not work.
I found that the scope of set statement could not be beyond the loop in jinja2 document(http://jinja.pocoo.org/docs/2.9/templates/#id12).
Please keep in mind that it is not possible to set variables inside a block and have them show up outside of it. This also applies to loops. The only exception to that rule are if statements which do not introduce a scope. As a result the following template is not going to do what you might expect:
{% set iterated = false %}
{% for item in seq %}
{{ item }}
{% set iterated = true %}
{% endfor %}
{% if not iterated %} did not iterate {% endif %}
It is not possible with Jinja syntax to do this.
It is true that global variables are generally not within scope inside for loops in jinja, which is a surprise to many (users of Python, Java, etc).
However, the workaround is to declare a dictionary object in that outer scope, which is then available in the for-loop:
{% set foundItem = { 'found': False } %}
{% for item in seq %}
{%- if item.form == "graph" %}
{%- if foundItem.update({'found':True}) %} {%- endif %}
{%- endif %}
{% endfor %}
{% if not iterated %} did not iterate {% endif %}
{% if foundItem.flag %} pull in graph CSS/JS {% endif %}

Django CMS – Show different content for users and guests in same template

I would like to have different content for users and guests in my home page's template using Django 1.9 and Django CMS 3.3.1.
It could be acomplished by making subpages and showing the corresponding content in the ancestor based on authentication conditional, but that makes the page structure overly complicated.
Is there an easy way of adding these placeholders straight to the template?
I have tried this:
{% extends "base.html" %}
{% load cms_tags %}
{% block title %}{% page_attribute "page_title" %}{% endblock title %}
{% block content %}
{% if not user.is_authenticated %}
{% placeholder "guests" %}
{% endif %}
{% if user.is_authenticated %}
{% placeholder "authenticated" %}
{% endif %}
{% placeholder "content" %}
{% endblock content %}
But as I am authenticated when I'm editing the content, I cannot access the guests placeholder.
Try this:
{% block content %}
{% if request.toolbar.build_mode or request.toolbar.edit_mode %}
{% placeholder "guests" %}
{% placeholder "authenticated" %}
{% else %}
{% if not user.is_authenticated %}
{% placeholder "guests" %}
{% endif %}
{% if user.is_authenticated %}
{% placeholder "authenticated" %}
{% endif %}
{% endif %}
{% placeholder "content" %}
{% endblock content %}
I have some experience with Django CMS but don't know if this will work. The idea is to check if we're in edit mode by inspecting corresponding request variables. See this answer.
Update by #V-Kopio:
The answer given above works fine in practice but Django warns about dublicate placeholders. This can be avoided by combining the if and else blocks:
{% block content %}
{% if not user.is_authenticated or request.toolbar.build_mode or request.toolbar.edit_mode %}
{% placeholder "guests" %}
{% endif %}
{% if user.is_authenticated %}
{% placeholder "authenticated" %}
{% endif %}
{% placeholder "content" %}
{% endblock content %}

issue with nested if? [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Django templates syntax error
When i am using {% if request.user.is_authenticated %} condition for redirection on this code it throws error Invalid block tag: 'else'
{% if request.user.is_authenticated %}
{% extends "pages/page.html" %}
{% load mezzanine_tags shop_tags i18n %}
{% block body_id %}category{% endblock %}
{% block main %}{{ block.super }}
{% regroup products by category as products_by_category %}
{% for c in products_by_category %}
......
{% for p in c.list %}
......
{% if p.num_in_stock == None %}
...
{% else %}
{% if p.num_in_stock < 4 %}
...
{% endif %}
{% endif %}
.....
{% endfor %}
......
{% endfor %}
{% endblock %}
{% else %}
<script>
window.location="/stylequiz/";
</script>
If i am using this script then it gives no error
{% if request.user.is_authenticated %}
<h1>welcome</h1>
{% else %}
<script>
window.location="/stylequiz/";
</script>
{% endif %}
I think there must be a problem with nested if .
You can't put {%extends%} tag inside {%if%}. It should be first tag in the template.
From django docs Template inheritance
If you use {% extends %} in a template, it must be the first template tag in that template. Template inheritance won't work, otherwise.

How to use a for loop inside a Django conditional?

Basically if there is a certain GET parameter in the url (in this case "latest") I want to slice the object list by a different number than the usual. But doing this:
{% if 'latest' in request.GET %}
{% for object in object_list|slice:"22" %}
{% else %}
{% for object in object_list|slice:"10" %}
{% endif %}
// blah blah
{% endfor %}
causes a syntax error since Django expects a closing endfor instead of the else. Is there any way to use for loops inside conditionals?
You need to have a body in your for loop.
{% if 'latest' in request.GET %}
{% for object in object_list|slice:"22" %} {{ object }} {% endfor %}
{% else %}
{% for object in object_list|slice:"10" %} {{ object }} {% endfor %}
{% endif %}
Without it, you're saying the equivalent of the following Python code:
if 'latest' in request.GET:
for object in slice(object_list, 22):
#No code here
else:
for object in slice(object_list, 10):
#No code here
which obviously is an error.
Just close the for loop inside each conditional:
{% if 'latest' in request.GET %}
{% for object in object_list|slice:"22" %}
{{ object.name }}
{% endfor %}
{% else %}
{% for object in object_list|slice:"10" %}
{{ object.name }}
{% endfor %}
{% endif %}

Categories

Resources