Skip to main content

Customizing the Jinja Environment

Flask uses a specialized Jinja2 environment to integrate template rendering with its application and blueprint structure. This integration is primarily handled by the Environment class and the DispatchingJinjaLoader, which together ensure that templates are resolved correctly across multiple directories and that standard Flask globals are available in every template.

The Environment Class

The flask.templating.Environment class is a subclass of jinja2.Environment. Its primary role is to maintain a reference to the Flask application instance and ensure the correct loader is used.

class Environment(BaseEnvironment):
def __init__(self, app: App, **options: t.Any) -> None:
if "loader" not in options:
options["loader"] = app.create_global_jinja_loader()
BaseEnvironment.__init__(self, **options)
self.app = app

When initialized, if no loader is provided in the options, it calls app.create_global_jinja_loader(), which returns a DispatchingJinjaLoader.

Blueprint-Aware Template Loading

Template resolution in Flask is handled by flask.templating.DispatchingJinjaLoader. Unlike a standard Jinja loader that might look in a single directory, this loader iterates through the application's own template folder and then through the template folders of all registered blueprints.

The resolution order is defined in _iter_loaders:

def _iter_loaders(self, template: str) -> t.Iterator[tuple[Scaffold, BaseLoader]]:
loader = self.app.jinja_loader
if loader is not None:
yield self.app, loader

for blueprint in self.app.iter_blueprints():
loader = blueprint.jinja_loader
if loader is not None:
yield blueprint, loader
  1. Application Templates: The loader first checks the application's jinja_loader (usually pointing to the templates folder in the app root).
  2. Blueprint Templates: If not found, it iterates through all registered blueprints in the order they were registered and checks their respective jinja_loader folders.

Debugging Template Resolution

If you are unsure why a specific template is being loaded (or why it isn't), you can set the EXPLAIN_TEMPLATE_LOADING configuration option to True. When enabled, the DispatchingJinjaLoader will use _get_source_explained to log every attempt made to find the template across the app and blueprints.

Environment Initialization

The Jinja environment is not created immediately when the Flask object is instantiated. Instead, it is created lazily the first time app.jinja_env is accessed. The creation logic resides in App.create_jinja_environment (found in src/flask/app.py).

Default Globals and Policies

During initialization, Flask injects several standard variables into the Jinja global namespace:

  • url_for: The URL generation function.
  • get_flashed_messages: For retrieving flashed messages.
  • config: The current application configuration.
  • request: The current request object.
  • session: The current session object.
  • g: The application context global.

It also configures the JSON serialization policy:

rv.policies["json.dumps_function"] = self.json.dumps

Configuration Options

The environment's behavior is influenced by app.jinja_options. Key settings include:

  • Autoescaping: Controlled by select_jinja_autoescape, which defaults to enabling autoescape for .html, .htm, .xml, and .xhtml files.
  • Auto-reloading: Controlled by the TEMPLATES_AUTO_RELOAD config. If set to None, it follows the application's debug mode.

Customizing the Environment

There are two primary ways to customize the Jinja environment in this codebase.

1. Modifying jinja_options

You can modify the jinja_options dictionary on the application instance before the environment is initialized.

app = Flask(__name__)
app.jinja_options.update({
"trim_blocks": True,
"lstrip_blocks": True,
})

2. Overriding the Environment Class

For more advanced customization, you can provide your own Environment subclass by overriding the jinja_environment attribute on a custom Flask class.

from flask import Flask
from flask.templating import Environment

class CustomEnvironment(Environment):
def __init__(self, app, **options):
# Custom logic here
super().__init__(app, **options)

class CustomFlask(Flask):
jinja_environment = CustomEnvironment

app = CustomFlask(__name__)

This approach allows you to change how the environment is constructed or add custom methods and properties to the environment itself. Regardless of the customization method, the environment remains integrated with Flask's DispatchingJinjaLoader unless explicitly overridden in create_jinja_environment.