Python Flask Jinja Templating

So you may have seen this post:

Which goes over the basics of Flask. There is a little bit about templating, but I’d like to go into a bit more detail about it.


render_template

In a Flask app, you have your main file, a static folder, and the templates folder. You can use render_template to render HTML files from inside of the templates folder.

app.py

from flask import Flask, render_template

app = Flask(__name__)


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

Passing variables to HTML

Using the render_template function, you can pass variables to your HTML files with Jinja using the {{ <var> }} syntax.

app.py

from flask import Flask, render_template
from replit import web

...

@app.route('/')
def index():
    return render_template('index.html',
                           username=web.auth.name,
                           userid=web.auth.user_id
                          )

templates/index.html

<body>
    <main>
        <h1>Templating</h1>
        <!-- This is the jinja syntax to use variables passed in from render_template. -->
        <h2>Hello, {{ username }}!</h2>
        <p>Your Replit UserID is {{ userid }}.</p>
    </main>
</body>

Creating variables

Variables can be created in HTML files using the {% set <var> = <value> %} syntax.

templates/index.html

<body>
  <main>
    {% set var = "Hello, world!" %}
    <p>{{ var }}</p>
  </main>
</body>

Variable filters

You can apply filters such as safe (allows you to render HTML in a string), length (equivalent of len(<var>)), title (equivalent of <var>.title()), and more, to variables. For example:

app.py

...

phrase = 'Hello, world!'


@app.route('/')
def index():
    return render_template('index.html', phrase=phrase)

templates/index.html

<body>
  <main>
    <p>"{{ phrase }}" is {{ phrase | length }} characters long.</p>
  </main>
</body>

And use multiple filters at once:

...

<p>{{ number | abs | string }}</p>

...

You can also make your own variable filters:

app.py

...

@app.route('/')
def index():
    return render_template('index.html', phrase='Hello sus world!')


@app.template_filter('my_filter')
def my_filter(string):
    return string.replace('sus', 'ඞ')

templates/index.html

...

<p>{{ phrase | my_filter }}</p>

...

If statements

You can write if statements with Jinja too, using the {% if <condition> %} {% endif %} syntax.

app.py

from flask import Flask, render_template, request
from replit import web

admins = [
    17197014 # QwertyQwerty88
]

app = Flask(__name__)


@app.route('/')
def index():
    return render_template('index.html',
                           username=web.auth.name,
                           userid=web.auth.user_id
                          )

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


@app.route('/admin')
def admin():
    return render_template('admin.html',
                           admins=admins)


@app.route('/bad')
def bad():
    return "SMH, you're not an admin."

templates/admin.html

<body>
    <!-- Jinja if statement -->
    {% if not userid %}
        <script>location.replace('/login')</script>
    {% elif userid not in admins %}
        <script>location.replace('/bad')</script>
    {% endif %}
    
    <main>
        <h1>Templating</h1>
        <!-- This is the Jinja syntax to use variables passed in from render_template. -->
        <h2>Hello, {{ username }}! You are an admin.</h2>
        <p>Your Replit UserID is {{ userid }}.</p>
    </main>
</body>

For loops

You can also write for loops with Jinja using the {% for <var> in <iterable> %} {% endfor %} syntax.

templates/admin.html

<body>
    <!-- Jinja if statement -->
    {% if not userid %}
        <script>location.replace('/login')</script>
    {% elif userid not in admins %}
        <script>location.replace('/bad')</script>
    {% endif %}
    
    <main>
        <h1>Templating</h1>
        <!-- This is the Jinja syntax to use variables passed in from render_template. -->
        <h2>Hello, {{ username }}! You are an admin.</h2>
        <p>Your Replit UserID is {{ userid }}.</p>

        <h3>All admin UserIDs</h3>
        <ul>
            <!-- Jinja for loop syntax -->
            {% for admin in admins %}
                <li>{{ admin }}</li>
            {% endfor %}
        </ul>
    </main>
</body>

Components

To use components in Flask, you need to create HTML files in the templates folder (or a subfolder inside of it) and use the {% include '<file>.html' %} Jinja syntax. Variables passed from render_template will also work in components.

templates/components/header.html

<div class="user">
    <img class="pfp" src="{{ pfp }}" alt="PFP">
    <span class="username">{{ username }}</span>
</div>

templates/index.html

<body>
    <header>{% include 'components/header.html %}</header>

    <main>
        <h1>Templating</h1>
    </main>
</body>

Base Layout

Using the {% block <name> %} {% endblock %} and {% extend '<file>.html' %} syntax, you can create a base file for your other files to follow.

templates/base.html

<body>
    <header>
        {% block header %}{% endblock %}
    </header>

    <main>
        <h1>Templating</h1>
        {% block content %}{% endblock %}
    </main>
</body>

templates/index.html

{% extends 'base.html' %}

{% block title %}Home{% endblock %}

{% block header %}{% include 'components/header.html' %}{% endblock %}

{% block content %}
    <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
{% endblock %}

You can also take a look at the Flask Documentation on Templates:

https://flask.palletsprojects.com/en/2.3.x/tutorial/templates/

Or the Jinja documentation:

https://jinja.palletsprojects.com/en/3.1.x/

Or my example Repl:

https://replit.com/@QwertyQwerty88/Templating

4 Likes

This is very useful. I didn’t even know that you could use components!

1 Like

yeah, I only learned about it today while asking Phind how I can get something similar to React 'cause I wanted to reuse the same HTML in multiple files.

1 Like

Cool!
In the repl of mine you looked at, instead of doing anything in html files, basically everything is a function as most of my components need arguments (I wish jinja supported that).

1 Like

that works tho

So in your components you can use {{ var }} syntax and in render_template use ('file.html', var=var)

1 Like

Yeah but I mean with the include syntax. Although in my case, when all the code is python functions returning HTML, there is no use in using HTML files.

you don’t need to put them in the include statement, tho

like I’ll give ya an example tmrw cause it’s late rn but I’ll show you

Yeah ik you can do smth like:


main.py

from flask import Flask, render_template

app = Flask(__name__)

def testfunc():
  return "test!"

@app.route("/")
def index():
  return render_template("index.html", testfunc = testfunc)

app.run(host="0.0.0.0")

templates/index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>testfunc test</title>
  </head>
  <body>
	<h1>testfunc()</h1>
  </body>
</html>

1 Like

actually this isn’t right, you’re missing the curly braces


anyway @MiloCat I meant something like this

main.py

from flask import Flask, render_template

app = Flask(__name__)

myArg = "This was passed through to render_template and used in a component!"


@app.route("/")
def index():
  return render_template("index.html", arg=myArg)


app.run(host="0.0.0.0", port=81)

templates/components/component.html

<p>{{ arg }}</p>

templates/index.html

<!DOCTYPE html>
<html lang="en">
    
<head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Templating</title>
</head>
    
<body>
    <main>
        <h1>Templating</h1>
        {% include 'components/component.html' %}
    </main>
</body>
    
</html>
2 Likes

Oh ok. Sorry for my mistake in the previous response, it was like 3am :skull:

1 Like

Jinja also has methods of using handy Python functions such as len and title. Note that in some but not all cases, you don’t use normal Python syntax, but rather "word"|title to capitalize word. You can have a variable named title and still use the function, unlike in regular Python. I find Jinja to be extremely useful.

1 Like

Yes. I also forgot to mention creating variables with {% set <var> = <value> %}.

This is a wiki though, so feel free to add anything else I missed.

3 Likes

I rarely create variables in Jinja. Jinja is handy but takes longer to code than normal Python, so I do everything I can in main.py and use Jinja mostly to display data I’ve already processed. It is useful to know that though.

3 Likes

Don’t you need

global myArg
1 Like

Why would you? It works without. Why do you think it’s needed?

My mistake, I thought you needed it

1 Like

Another thing. In templates, sometimes you may use it for dynamically rendering HTML.

Like an example:

In HTML Code:

<p>Current Status:</p>
{{ status }}

In Python Code:

return render_template(“main.html”, status=“<p>All good!</p>”)

That would show like this:

Current Status:
<p>All good!</p>

The reason is the template engine has automatic XSS protection. Any HTML tags will be automatically filtered to not render on the page and instead render as raw text. To fix this, on the variable you can add the “safe” tag, like this:

{{ status | safe }}

And this tells the templating engine to not filter the text for XSS protection.

2 Likes

Wow, I didn’t know that. Cool!

1 Like

Why did you include a link to cracked (and potentially dangerous) software?

4 Likes

Who included what hazardous link?

3 Likes