-1

I am writing a web application which accepts user input in an HTML form written in Jinja2. Based on the input, I set variables defined in Jinja2 which I then want to pass as positional arguments to another function defined in either my __init__.py file or main.py file.

Python version==3.6.8  
Flask==2.0.1  
Jinja2==3.0.1  

I attempted many ways to achieve this but could not so (got an undefined error when trying to call the function), including following the suggestions in this thread: Call a python function from jinja2

The file structure of my web app looks like so:
https://prntscr.com/1rlt3cl (I can't post images because I don't have enough reputation points so I uploaded it here).

I am receiving user input in my form.html file which I pass to the data.html file.

<form action="/data" method = "POST">
    <p>Various inputs<input type = "number" name = "random" /></p>
    <p><input type = "submit" value = "Submit" /></p>
</form>

From the data.html file, I want to accept the values inputted by the user, and call a function which sits in all of my __init__.py files (wasn't sure which one Flask actually looks at):

{% set ns1 = namespace(random=None) %}   
{% for key,value in form_data.items() %}
    {% if key ==  "iterations" %}
        {%- set ns1.iterations = value -%}
    {% endif %}
{% endfor %}

{{ clever_function(random) }}

clever_function is defined in the __init__.py file as so:

from jinja2 import Template 
def clever_function(): 
    return "Hello"
    
template = Template("{{ clever_function() }}")
template.globals['clever_function'] = clever_function  

When running my application from webapp/main.py which renders the form / data templates like so:

@app.route('/form')
def form():
    return render_template('form.html')
 
@app.route('/data', methods = ['POST', 'GET'])
def data():
    if request.method == 'GET':
        return f"The URL /data is accessed directly. Try going to '/form' to submit form"
    if request.method == 'POST':
        form_data = request.form
        return render_template('data.html',form_data = form_data)  

I receive the following exception:

jinja2.exceptions.UndefinedError: 'clever_function' is undefined
bad_coder
  • 11,289
  • 20
  • 44
  • 72

1 Answers1

0

The code you use is a brief description of the exclusive use within a directly defined template.
To use a custom function within a template used by render_template, you have to add it to the dictionary globals of the jinja environment.

from flask import Flask
from flask import render_template, request

def clever_function(value):
    return value**2

app = Flask(__name__)
app.jinja_env.globals.update(clever_function=clever_function)

@app.route('/form')
def form():
    return render_template('form.html')

@app.route('/data', methods=['POST'])
def data():
    return render_template('data.html', form_data=request.form)

From now on, this function can be called in every template which is loaded and called up with this environment.

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title></title>
  </head>
  <body>
    <p>Result: {{ clever_function(form_data.get('random', 0) | int) }}</p>
  </body>
</html>

Please keep in mind that the defined function must accept parameters if you want to call it up with any.


Errors have also crept in when using the namespace object. You should take another look at the documentation for the assignments in jinja2.

Here is a small example based on your code.

{% set ns1 = namespace(random=0) %}
{% for k,v in form_data.items() %}
  {% if k == 'random' %}
    {% set ns1.random = v | int %}
  {% endif %}
{% endfor %}
<p>Value is {{ns1.random}}</p>
Detlef
  • 6,137
  • 2
  • 6
  • 24