166

The jinja API document at pocoo.org states:

The simplest way to configure Jinja2 to load templates for your application looks roughly like this:

from jinja2 import Environment, PackageLoader
env = Environment(loader=PackageLoader('yourapplication', templates'))

This will create a template environment with the default settings and a loader that looks up the templates in the templates folder inside the yourapplication python package.

As it turns out, this isn't so simple because you have to make/install a python package with your templates in it, which introduces a lot of needless complexity, especially if you have no intention of distributing your code.

I found these related questions about doing so, but the answers are vague and unsatisfying:

How can I load the template directly from the filesystem, not as a resource in a package?

Karl Knechtel
  • 62,466
  • 11
  • 102
  • 153
Juan Tomas
  • 4,905
  • 3
  • 14
  • 19

5 Answers5

207

Use a FileSystemLoader instead of a PackageLoader. Suppose there is a python file in the same directory as the template:

./index.py
./template.html

This index.py will find the template and render it:

#!/usr/bin/python
import jinja2

templateLoader = jinja2.FileSystemLoader(searchpath="./")
templateEnv = jinja2.Environment(loader=templateLoader)
TEMPLATE_FILE = "template.html"
template = templateEnv.get_template(TEMPLATE_FILE)
outputText = template.render()  # this is where to put args to the template renderer

print(outputText)

In the introduction, the PackageLoader approach seems to be presented as the default, "simplest" method; however, there is also a section which discusses all the built-in loaders.

Karl Knechtel
  • 62,466
  • 11
  • 102
  • 153
Juan Tomas
  • 4,905
  • 3
  • 14
  • 19
  • 139
    Sort of ridiculous you can't load a template from a file in one line e.g. `jinja2.load_template('template.html')` – Matt Sep 22 '17 at 14:57
  • 4
    I always have a Wrapper that I just call Jinja2 in my applications where I put all this verbosity, then call it like: `Jinja2.render(template_name, data)` – Seraf Apr 08 '19 at 13:27
  • 24
    **Important security risk!** You almost certainly want to call `jinja2.Environment(loader=templateLoader, autoescape=True)`. Or see the [api docs](https://jinja.palletsprojects.com/en/2.10.x/api/) for more info. Just found out I ended up with a major XSS vulnerability from following this answer :/ – andrewdotn Aug 31 '19 at 13:57
  • Both links at top are broken. – sshow Nov 11 '20 at 15:25
135

A simpler way is to directly call the jinja2.Template constructor and use open to load the file:

from jinja2 import Template
with open('template.html.jinja2') as file_:
    template = Template(file_.read())
template.render(name='John')
Karl Knechtel
  • 62,466
  • 11
  • 102
  • 153
Cas
  • 6,123
  • 3
  • 36
  • 35
  • 5
    Unfortunately this does not allow setting up custom filters. The template loading generates an error during initialization because the custom filter doesn't exist yet. And this way you only have access to the environment (to include the filter) after initialization. – Ronan Paixão Jul 17 '19 at 19:09
51

Here is the one liner:

from jinja2 import Template

with open('template_file.j2') as f:
    template = Template(f.read())

Then you can render the template on another line, or for all in one line:

with open('template_file.j2') as f:
    rendered = Template(f.read()).render(var="TEXT")
Martin Thoma
  • 124,992
  • 159
  • 614
  • 958
bcarroll
  • 1,727
  • 16
  • 14
  • 2
    Sadly this will break if there is template inheritance, as Jinja won't be able to find the referenced templates. – Bemmu Jun 17 '19 at 05:13
  • 18
    But luckily this is simple and enough if you don't use inheritance, and just wan't to send some simple email for example.. :) – smido Jan 21 '20 at 21:44
  • 1
    The other major downside to this one liner is that you don't close the file. At worst, this leads to file corruption. Would highly recommend using a python context and making it two lines rather than one! – Nikhil Shinday Nov 08 '21 at 15:55
  • 2
    @NikhilShinday Failing to close a file that is open for reading (the default mode of [`open`](https://docs.python.org/3/library/functions.html#open)) will absolutely never lead to file corruption. – Jonathon Reinhart Apr 14 '22 at 04:26
  • 1
    It's not clear to me how this adds anything over Cas's answer. – Karl Knechtel Jan 10 '23 at 01:16
  • @NikhilShinday the `with` context manager closes the file once the template rendering is done. Cf. "https://docs.python.org/3/library/io.html#io.IOBase" search for "context manager" – hraban Aug 19 '23 at 10:48
21

If using Python 3.4+ and Jinja2 - v2.11+ -- we can combine python's pathlib and Filesystem to simplify the flow

from pathlib import Path
...

p = Path(__file__).parent.parent / 'templates' # sample relative path
env = Environment(
    loader=FileSystemLoader(Path(p)))
template = env.get_template('your_file.jinja2')

I am not comfortable with using directly Template(file) since Jinja's template inheritance processing may not work well.

Pathlib support is only added in latest version of Jinja - v2.11+

Sairam Krish
  • 10,158
  • 3
  • 55
  • 67
  • 5
    `Path(p)` is redundant. – Björn Lindqvist Mar 17 '22 at 13:07
  • This is a neat solution for when you get your pathname on the command line, and it could be absolute or relative: use `p = Path(sys.argv[1])`, then `FileSystemLoader(p.parent)` and `env.get_template(p.name)`. Works for both relative and absolute pathnames. – hraban Aug 19 '23 at 10:54
2
from jinja2 import Environment, select_autoescape, FileSystemLoader

env = Environment(loader=FileSystemLoader(
searchpath=folder_contain_list_html), autoescape=select_autoescape(['html', 'xml']))

template = env.get_template('file_name_detail_template')

body_html = template.render(**args)

send_email(body_html)
Shaido
  • 27,497
  • 23
  • 70
  • 73
Mark
  • 21
  • 3