Jinja Macros: passing the local variables - python

How can I pass the all local variables in a template to a macro?
I can pass var1, var 2 to macro_function like that in test.html
{% from 'macro.html' import macro_function %}
{{ macro_function(var1, var2) }}
is there something like
{{ macro_function(**locals) }}
so that I can pass all local variables in test.html to the macro?

Not sure why you would want to do that.
I would say keep definitions of template scoped variables to a minimum.
And if you need a set of variables, you can pass a dict or an object holding all relevant key/value pairs from the function that renders the macro. Then in turn pass the dict/object to the macro.

Related

Context behavior for jinja2 blocks with included files

I have difficulties understanding the context behavior when including files in Jinja2 blocks. See this minimal example:
base.jinja2
{% set foo="bar" %}
{% block content %}
{% include 'include.jinja2' %}
{% endblock content %}
include.jinja2
{{ foo }}
Rendering base.jinja2 produces the error foo' is undefined.
When I move the declaration of foo into the content block, the code renders correctly. So does it, when I move the include statement outside the content block, when I remove the content block around the include statement, or when I replace the include statement with the file's contents.
Why is that? How can I use the global foo variable in the include file inside the content block?
Look at the link, section Block Nesting and Scope:
Blocks can be nested for more complex layouts. However, per default
blocks may not access variables from outer scopes:
The reason for this is that if the block is replaced by a child
template, a variable would appear that was not defined in the block or
passed to the context.
According to the document and starting with Jinja 2.2 you can change this behavior using scoped.
{% set foo="bar" %}
{% block content scoped %}
{% include 'include.jinja2' %}
{% endblock content %}
I agree that this is somehow weird, compared to what we are used to. But this is an intended feature.
Generally speaking, include is only passed the context. Since 2.1 it is
passed a derived context that is the original context + all the local
variables used before the include statement. This also means that if
you just use a variable from an outer scope, Jinja2 will track it as
local now and pass it to the derived context.

How to pass an argument to a function in html [duplicate]

I'm passing to Django's template a function, which returns some records.
I want to call this function and iterate over its result.
{% for item in my_func(10) %}
That doesn't work.
I've tried to set the function's return value to a variable and iterate over the variable, but there seems to be no way to set a variable in a Django template.
Is there any normal way to do it?
You cannot call a function that requires arguments in a template. Write a template tag or filter instead.
if you have an object you can define it as #property so you can get results without a call, e.g.
class Item:
#property
def results(self):
return something
then in the template:
<% for result in item.results %>
...
<% endfor %>
I'm passing to Django's template a function, which returns me some records
Why don't you pass to Django template the variable storing function's return value, instead of the function?
I've tried to set fuction's return value to a variable and iterate over variable, but there seems to be no way to set variable in Django template.
You should set variables in Django views instead of templates, and then pass them to the template.
By design, Django templates cannot call into arbitrary Python code. This is a security and safety feature for environments where designers write templates, and it also prevents business logic migrating into templates.
If you want to do this, you can switch to using Jinja2 templates (http://jinja.pocoo.org/docs/), or any other templating system you like that supports this. No other part of django will be affected by the templates you use, because it is intentionally a one-way process. You could even use many different template systems in the same project if you wanted.
What you could do is, create the "function" as another template file and then include that file passing the parameters to it.
Inside index.html
<h3> Latest Songs </h3>
{% include "song_player_list.html" with songs=latest_songs %}
Inside song_player_list.html
<ul>
{% for song in songs %}
<li>
<div id='songtile'>
<a href='/songs/download/{{song.id}}/'><i class='fa fa-cloud-download'></i> Download</a>
</div>
</li>
{% endfor %}
</ul>

Jinja2 dynamic variable building

My jinja template gets an object which has many variable names, this attributes vary and so their names, I am looking for a way to access this attributes based on a prefix and a for loop:
{% for i in Object.vars %}
<h1> {{ Object.attribute_ + i }} </h1>
{% endfor %}
I'm trying to access Object.attribute_1, Object.attribute_2 and so on. the code above of course won't work, but I can't think on a way of doing this.
Keep in mind that doing too much logic in your template files will cause (long term) issues to maintain your code.
I would say, keep your logic outside of the template and create a list of your objects before rendering the template, using the getattr() function:
for i in Object.vars:
list_of_objects.append(getattr(Object, 'attribute_' + i))
Now when rendering the template pass the list to like that:
render_template('page.html', list_of_objects=list_of_objects)
The canonical way to solve problems like this is to pass a structure such as a list or dict. Dynamic variable names are almost always a terrible idea.

How to have variable defaults in templates?

I want to pass variable defaults to my templates in case of the actual variable is None.
Here is what I mean:
{{ value|default:"Default" }} ## General case.
How can I replace "Default" with my custom variable ?
Is it possible to replace it with another variable ?
Something like :
{{ value|default:{{value2}} }} ## OR Something similar
You can just put another variable instead of constant string like this:
{{ value|default:backup_var }}
I've just tested this, it works fine. Here is my template piece:
<i>{{ my_var|default:user.username}}</i>
I have no my_var defined, so it evaluates to:
<i>admin</i>
Summing up, django parser accepts not only constant values, but also variables as parameters to filters also. But not expressions (I couldn't figure out how to make my expression parsed. But if you need it, you may alsways use {% with var=expr %} ... {% endwith %} for complex situations)

How to call function that takes an argument in a Django template?

I'm passing to Django's template a function, which returns some records.
I want to call this function and iterate over its result.
{% for item in my_func(10) %}
That doesn't work.
I've tried to set the function's return value to a variable and iterate over the variable, but there seems to be no way to set a variable in a Django template.
Is there any normal way to do it?
You cannot call a function that requires arguments in a template. Write a template tag or filter instead.
if you have an object you can define it as #property so you can get results without a call, e.g.
class Item:
#property
def results(self):
return something
then in the template:
<% for result in item.results %>
...
<% endfor %>
I'm passing to Django's template a function, which returns me some records
Why don't you pass to Django template the variable storing function's return value, instead of the function?
I've tried to set fuction's return value to a variable and iterate over variable, but there seems to be no way to set variable in Django template.
You should set variables in Django views instead of templates, and then pass them to the template.
By design, Django templates cannot call into arbitrary Python code. This is a security and safety feature for environments where designers write templates, and it also prevents business logic migrating into templates.
If you want to do this, you can switch to using Jinja2 templates (http://jinja.pocoo.org/docs/), or any other templating system you like that supports this. No other part of django will be affected by the templates you use, because it is intentionally a one-way process. You could even use many different template systems in the same project if you wanted.
What you could do is, create the "function" as another template file and then include that file passing the parameters to it.
Inside index.html
<h3> Latest Songs </h3>
{% include "song_player_list.html" with songs=latest_songs %}
Inside song_player_list.html
<ul>
{% for song in songs %}
<li>
<div id='songtile'>
<a href='/songs/download/{{song.id}}/'><i class='fa fa-cloud-download'></i> Download</a>
</div>
</li>
{% endfor %}
</ul>

Categories