how to call a global variable in a function in flask? - python

Hello guys, small part of my code here, i have result and pred variables. And in the html part i have this code:
<div style = "margin-Top:100px;" class="container">
{{ results[pred] }}
</div>
And i get this error. So how can i fix it?
So i thought the variable in html calling like that in flask. Am i wrong?

results[pred] uses 2 variables to resolve:
results, dict
pred, the lookup in the dict
It seems, that u only need the value of pred in results. You can do this by using return render_template('index.html', var=results[pred]) and {{ var }}
Edit: In your case pred is static in python so you should only return the mandotory value of the results dict to the template.

You can’t pass parameter like this.
In render_template pass result=result
And then in the template you can use it like you write - result[pred]

Related

how to concate variables in url_for jinja2 flask? [duplicate]

I have a route defined like this:
#app.route('/magic/<filename>')
def moremagic(filename):
pass
And now in a template I want to call that route using url_for() like so:
<h1>you uploaded {{ name }}<h1>
Click to see magic happen
I have tried:
Click to see magic happen
That throws a jinja2.TemplateSyntaxError: expected token ':' got }
Can anyone suggest how to get the {{ name }} that appears in the template into the url_for() so that when I click I call the correct app.route?
Everything inside the {{ ... }} is a Python-like expression. You don't need to use another {{ ... }} inside that to reference variables.
Drop the extra brackets:
<h1>you uploaded {{ name }}<h1>
Click to see magic happen
(Note that the url_for() function takes the endpoint name, not a URL path; the name defaults to the name of the function, moremagic in your example).

Calling a jinja2 filter with only the context as an argument

I'm trying to render a jinja2 template in my service (not using flask, just pure jinja2 and constructing a jinja2 Environment.
Let's say I have the following filter defined
import jinja2
#jinja2.contextfilter
def my_function(context):
if context.get('my_var'):
return "hello"
else:
return "world"
Super simplified example, but the point of it is that I have some logic that conditionally returns a value based on some variable passed into the context.
Also, I'm using jinja2 2.11 or something like that, which is why I'm using #contextfilter instead of #pass_context.
I've added this filter to my environment using env.filters['my_function'] = my_function
In rendering the template, I'm calling
template = env.get_template('my_template.html')
template.render({'my_var': 'some_value'})
where the template might look something like
... some html here
{{ my_function }}
... some more html
This doesn't actually return "hello", and instead just is empty/blank.
I managed to get it by passing in a dummy variable
#jinja2.contextfilter
def my_function(context, value):
.... code is the same
And then in the template, I call it with {{ 'hi' | my_function }}. But obviously this is just a hack and not very desirable.
So my question is, how can I call a jinja2 filter function that only takes the context in as an argument? I've tried {{ my_function() }} which returns the error UndefinedError: 'my_function' is undefined, and {{ | my_function }}, which returns the error TemplateSyntaxError: unexpected '|'`
Or is there some other jinja2 construct I should be using?
Edit: my suspicion is that jinja2 uses the | to identify a filter vs a variable, and since I don't have |, then it tries to just render the variable my_function from the context, and since it doesn't exist in the context, it just outputs an empty string.
Jinja2 calls these kind of functions global functions (like range()), not filters. Just change filters to globals in this line:
env.globals['my_function'] = my_function
And then you can call your function in the templates: {{ my_function() }}.

What is context in Jinja2?

jinja_code.py
import jinja2
env=jinja2.Environment(loader=FileSystemLoader(searchpath="./"))
template=env.get_template('file.j2')
render_template=template.render(test1="TEST1",test2="TEST2")
print(render_template)
file.j2
{{ context.test1 }}
I'm learning Jinja2 and I understood that context are the variables that are passed to Template but when I execute the above code, i get the below error
jinja2.exceptions.undefinederror: 'context' is not defined
I've read the docs and I couldn't understand it completely. Can you please explain what is context and how it is used to access the variables?
Context contains the dynamic content that you want to inject in your template when it is being rendered.
In your example, the file file.j2 must have the following content:
{{ test1 }}
As context is not a variable but the collection of all variables you pass to the template. test1 and test2 are part of the context.

Calling a method with the multiple arguments in a template

How can I call a method, pass multiple parameters to it and render the result in a html template? I can't find a simple solution which works in all circumstantials. I'm aware about #register.filter and #register.simple_tag but there's alway an error being thrown. If it's not thing, it's another. For example, simple_tag works when I render it, but throws an error when I call if.
So how can I call a method from an html template in the following ways:
{{method1(a, b)}}
{% if method1(a, b) == some_value %} {{method1(a, b)}} {%endif%}
Note I don't need advice like "you're doing something wrong, you should change your architecture" because that's not the case here. I just a simple way to do what I want. In Rails, for example, I can do it very, very easily.
You can use an assignment tag to assign the value received from the template tag into a variable which then can be used anywhere in the template.
#register.assignment_tag
def method1(a,b):
...
Then in your template you can call this template tag and assign its value to a variable method_value.
{% method1 a b as method_value %}
Then use this variable in the template.
{{method_value}}
{% if method_value == some_value %} {{method_value}} {%endif%}

Keep undefined variables

I am interested in rendering a template in multiple steps or keeping the tags for the undefined variables in Jinja2. I believe this would mean not only creating the 'UndefinedSilent" class (so the template won't crash on missing data) but also keeping the tags with the appropriate variable names if they are missing.
Example:
Let's say we have the name = "Test" included in the context, but quantity is missing.
Givent the following template:
<p>{{name}} has {{quantity}}</p>
After rendering, I need the template to become:
<p>test has {{quantity}}</p>
Does anyone know if this is achievable?
Providing DebugUndefined to named parameter undefined in the env, apparently does the trick. The rendered template preserves the {{<undefined variable}}.
Like here:
from jinja2 import Environment, BaseLoader, DebugUndefined
rtemplate = Environment(loader=BaseLoader,undefined=DebugUndefined).from_string("{{ a }} is defined, but {{ b}} is undefined")
print(rtemplate.render({"a":"a"}))
The result is:
a is defined, but {{ b }} is undefined
It is achievable using the default built-in filter.
<p>{{name|default('{{name}}')}} has {{quantity|default('{{quantity}}')}}</p>
The drawback is that the code becomes uglier and the variable names are duplicated, thus reducing maintainability.
Here is another approach that preserves undefined double curly expressions after rendering, including those that contain "multilevel" (dot-notated) references as well as any others.
The answer provided by Willy Pregliasco does not support preservation of undefined list types, eg {{ some_list[4] }} which is something I required. The below solution addresses this, as well as all possible schema types.
The idea is to parse the input template and try to resolve each expression with the provided context. Any that can not be resolved, we replace with a double curly expression that simply resolves to the original expression as a string.
Pass your template and context through the below preserve_undefineds_in_template function before calling render:
from jinja2 import Template, StrictUndefined, UndefinedError
import re
def preserve_undefineds_in_template(template, context):
patt = re.compile(r'(\{\{[^\{]*\}\})')
j2_expressions = patt.findall(template)
for j2_expression in set(j2_expressions):
try:
Template(j2_expression, undefined=StrictUndefined).render(context)
except UndefinedError:
template = template.replace(j2_expression, f"{{% raw %}}{j2_expression}{{% endraw %}}")
return template
Example:
template = """hello {{ name }}, {{ preserve_me }} {{ preserve.me[2] }}"""
context = { "name": "Alice" }
# pass it through the preserver function
template = preserve_undefineds_in_template(template, context)
# template is now:
# hello {{ name }}, {% raw %}{{ preserve.me }}{% endraw %} {% raw %}{{ preserve.me.too[0] }}{% endraw %}
# render the new template as normal
result = Template(template).render(context)
print(result)
The output is:
hello Alice, {{ preserve_me }} {{ preserve.me[2] }}
I also wated the same behaviour. The library jinja2schema provides the schema of variables needed for your template.
The steps for my solution are:
have a template
obtain the schema structure
give some data to fill
complete de data filling the missing items with the original string
from jinja2 import Template
import jinja2schema
def assign(schema, data, root=''):
'''Returns a corrected data with untouched missing fields
'''
out = {}
for key in schema.keys():
if isinstance(schema[key], (str, jinja2schema.model.Scalar)):
try:
out[key] = data[key]
except:
out[key] = f'{{{{ {root+key} }}}}'
elif isinstance(schema[key], (dict, jinja2schema.model.Dictionary)):
out[key]={}
try:
data[key]
except:
data[key] = {}
out[key] = assign(schema[key], data[key], root+key+'.')
return out
# original template
template_str = '<p>{{name}} has {{quantity}}</p>'
# read schema
schema = jinja2schema.infer(template_str)
# available data
data = {'name':'test'}
# data autocompleted
data_corrected = assign(schema, data)
# render
template = Template(template_str)
print(template.render(data_corrected))
The output is
<p>test has {{ quantity }}</p>
That was the intended result.
Hope it will help. This solution doesn't work with lists, but I think it is possible to extend the solution. You can also obtain the list of missing fields, if you need them.
This is the version with Template rather than Environment:
from jinja2 import Template, DebugUndefined
template = Template("<p>{{name}} has {{quantity}}</p>", undefined=DebugUndefined)
new_template = Template(template.render(name="Test"))
Thanks #giwyni
Another little hack if you just have few variables:
<p>{{name}} has {{ "{{quantity}}" }}</p>
The second replacement will replace to {{quantity}} so all is good ;)

Categories

Resources