Sanic Web Tutorial Part 2 - Templates

In Part 2 we will setup Sanic to render html templates.

Install and setup Jinja2 extension

We will enable our application to render Jinja2 templates. Jinja2 is a template engine developed by Armin Ronacher (the developer of Flask and many other things) and documentation for the project can be found here: https://palletsprojects.com/p/jinja/.

In order to render templates in Sanic we need to first install an extension. Head over to https://github.com/mekicha/awesome-sanic and find sanic-jinja2 (not jinja2-sanic, which hasn't be updated for some time). The direct link is: https://github.com/lixxu/sanic-jinja2. Install into your virtualenv

$ pip install sanic-jinja2

In order to enable this extension we need to tell Sanic so. Update our application to

from sanic import Sanic
from sanic import response
from sanic_jinja2 import SanicJinja2

app = Sanic(name='Sanic Web Tutorial')
jinja = SanicJinja2(app, pkg_name="main")


@app.route("/")
async def index(request):
    return response.html("<h1>Hello World</h1>")


if __name__ == "__main__":
    app.run(host="0.0.0.0", port=8000, debug=True)

Note that we also add the debut=True to the app.run() method. This will auto-restart the development server when we make code changes and provide more debugging info.

Additionally, notice that we set 'pkg_name="main"' when instantiating the jinja object? If you don't do this, you will likely get an error when running main.py. This seems to be a bug in the sanic-jinja2 extension though.

Our first html template

First, create a folder to hold all the templates:

$ mkdir templates

The name is important for now ('templates'). Inside this folder, crate an index.html page:

$ touch templates/index.html

And inside this index.html file, create a boilerplate HTML page:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>Sanic Web Tutorial</title>
  </head>
  <body>
    <h1>Sanic Web Tutorial</h1>
  </body>
</html>

and update our / route render this template instead of the plain text:

@app.route("/")
async def index(request):
    return jinja.render("index.html", request)

You should now be able to see our updated website on: http://0.0.0.0:8000/

Pass variables to the template

Lets say we wan't to pass our title as a parameter in the Python code to the template, how would we do that? Well, the jinja.render() method actually takes 3 arguments, the 3rd being the variables to pass. Lets update to:

@app.route("/")
async def index(request):
    return jinja.render("index.html", request, title="Sanic Web Title")

and update the template's h1 tag to be:

    <h1>{{ title }}</h1>

The {{ }} brackets is a Jinja2 template tag and it used as a standin for variables passed to the template.
Refresh your webpage on: http://0.0.0.0:8000/ and you should see the updated title.

What if you want to pass multiple variables to the template? Well, we can add multiple variables in the jinja.render function, like this:

@app.route("/")
async def index(request):
    return jinja.render("index.html", request, title="Sanic Web Title", desc="Something here")

and update the template's h1 tag to be:

    <h1>{{ title }}</h1>
    <p>{{ desc }}</p>

This can get quite ugly to read if you have to pass many variables, so we can pass an object instead:

@app.route("/")
async def index(request):
    context = {
        "title": "Sanic Web Title",
        "desc": "Something else here",
    }
    return jinja.render("index.html", request, context=context)

and we also need to update the html template to account for this:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>Sanic Web Tutorial</title>
  </head>
  <body>
    <h1>{{ context.title }}</h1>
    <p>{{ context.desc }}</p>
  </body>
</html>

and if you reload/refresh your website again, you should see that it still shows correctly.