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
- Application Templates: The loader first checks the application's
jinja_loader(usually pointing to thetemplatesfolder in the app root). - Blueprint Templates: If not found, it iterates through all registered blueprints in the order they were registered and checks their respective
jinja_loaderfolders.
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.xhtmlfiles. - Auto-reloading: Controlled by the
TEMPLATES_AUTO_RELOADconfig. If set toNone, it follows the application'sdebugmode.
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.