Jinja Templates

Author: Joe Hutcheson

Interactive applications can be created quickly and easily using Jinja templating in conjunction with a Flask server, a micro web framework written in Python. Jinja bills itself as “a fast, expressive, extensible templating engine”, all adjectives that we programmers like. The following simple demonstration will show how easy it can be to get a server set up to interact with a user utilizing dynamic templates.

Let’s create a simple application that will greet a user and take a coffee order. We’ll break the task into three steps:

  1. Greet the user and take their name.
  2. Using the user’s name, take their order.
  3. Repeat their order back to them or acknowledge that there is no order.

For each of these steps we will create a Jinja2 template as an HTML form. As we’ll see, we can essentially have our templates communicate with each other. First, let’s set up a simple flask server, making use of the render_template and request modules:

#!/usr/bin/python3
"""Alta3 Research
Flask and Jinja Templates"""

from flask import Flask, render_template, request

app = Flask(__name__) # creates an instance of our server object

We need to create an endpoint for each of our steps, each of which will render the according template to the browser. Let’s start with step 1:

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

Flask will expect to find templates in a folder called “templates” in the same directory as the script:

coffee_order_directory ├── coffee_order.py └── templates └── greet.html

Our template that is rendered, “greet.html”, needs to take in a user name.

<!doctype html>
<html>
  <head>
    <title>Greeting</title>
  </head>
  <body>
    <h1>Good morning! What is your name?</h1>
    <form action="/take_order" method="POST"> 
      <input type=text name=nm /> # when our user inputs their name, it's stored as the variable 'nm' 
      <input type=submit />
    </form>
  </body>
</html>

Notice that we assign an action to this form, essentially sending it to another endpoint, taking with it the information submitted by our user, i.e., their name. Let’s create that endpoint for step 2:

@app.route("/take_order", methods=["POST"])
def take_order():
    user = request.form.get("nm")
    return render_template("take_order.html", user=user) # this calls the template and sends it the "user" variable.

This endpoint will take a POST request and, using the flask.request module, access the form submission in which it will find the value of “nm”, which we will then set to the variable “user”. We will send that off to another template called “take_order.html”.

<!doctype html>
<html>
    <head>
        <title>Take order</title>
    </head>
    <body>
        <form action = "/serve_coffee" method = "POST">
            <p>Hello {{user}}!</p> # <= our "user" variable
            <p>Please enter your order below:</p>
            <input type= "text" name="order"/>
            <input type=submit />
        </form>
    </body>
</html>    

In Jinja templates, we access the value of variables with a set of double curly brackets: {{}}. The action on this form sends us to our next endpoint, step 3:

@app.route("/serve_coffee", methods = ["POST"])
def serve():
    if request.form.get("order"):
        order = request.form.get("order")
    else:
        order = None
    return render_template("serve.html", order=order)

This time, we call our final template and send it the order submitted through the previous template. We can add some logic to our Jinja template to acknowledge if we don’t receive input from our user:

<!DOCTYPE html>
<html>
  <head>
    <title>Order Confirmation</title>
  </head>
  <body>
    <h1>Order Confirmation</h1>
    {% if order %}
    <p>You have ordered {{ order }}.</p>
    {% else %}
    <p>You didn't order anything. More of a tea person?</p>
    {% endif %}
  </body>
</html>

This template uses Jinja conditionals, which are placed within the single curly bracket/percentage signs combo: {% %}.

Our final Python script would look like this:

#!/usr/bin/python3
"""Alta3 Research
Flask and Jinja Templates"""
    
from flask import Flask, render_template, request

app = Flask(__name__)

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

@app.route("/take_order", methods=["POST"])
def take_order():
    user = request.form.get("nm")
    return render_template("take_order.html", user=user)

@app.route("/serve_coffee", methods = ["POST"])
def serve():
    if request.form.get("order"):
        order = request.form.get("order")
    else:
        order = None
    return render_template("serve.html", order=order)

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=2224)

Our directory structure would look like this:

coffee_order_directory ├── coffee_order.py └── templates ├── greet.html ├── serve.html └── take_order.html

This is clearly a very simple demonstration, but shows the potential of what can be done using Jinja in conjunction with Flask. Once you understand how templates are rendered and can work in conjunction with each other, you can create much more exciting and elaborate applications.