As described in the documentation, since 4.1 the default behavior for template loading changed drastically.
If I understand it right, until 4.0 it worked like this:
That way, the template caching was seamlessly enabled in production which is great.
Now this ticket proposal was included and, if I get it correctly, the template loading method must be specified and it's not anymore tied to DEBUG setting, AND, by default are cached.
We want the original behavior so the frontend developer can see the changes without having to restart the app, and we also want the production deployment to have the caching enabled, so we did this:
develop_loaders = [
"django.template.loaders.filesystem.Loader",
"django.template.loaders.app_directories.Loader",
]
production_loaders = [
("django.template.loaders.cached.Loader", [
"django.template.loaders.filesystem.Loader",
"django.template.loaders.app_directories.Loader",
"path.to.custom.Loader",
])
]
TEMPLATES = [
{
"BACKEND": "django.template.backends.django.DjangoTemplates",
"DIRS": [
"templates",
],
"OPTIONS": {
"context_processors": [
"maintenance_mode.context_processors.maintenance_mode",
"django.template.context_processors.debug",
"django.template.context_processors.request",
"django.contrib.auth.context_processors.auth",
"django.contrib.messages.context_processors.messages",
"wagtail.contrib.settings.context_processors.settings",
],
"loaders": develop_loaders if DEBUG else production_loaders,
},
},
]
Which works, but I wonder, am I getting the situation correctly? Do you think this is a solid solution?.
Also it took me a while because when I read the changelog for 4.1 I didn't grasp that this change would have this impact (we never specified any loader in settings before) so we expected the default behavior to be respected, which led to looking at gunicorn and docker as the first suspicious culprits, etc... so I thought that this question might be useful for other people in a similar situation.
Yes, you are correct.
Yes, that config will work and is well written.
Here's why.
By default (as of Django 4 / Django 5) the Django TEMPLATES setting will automatically use a wrapper of django.template.loaders.cached.Loader unless you tell it not to.
This happens by default:
"This loader is automatically enabled if
OPTIONS['loaders']isn’t specified."https://docs.djangoproject.com/en/5.1/ref/templates/api/#django.template.loaders.cached.Loader
This default cache setting is cumbersome for local development, because to test any template change, you need to either...
Your solution is the best of both worlds: you get caching for production, but you totally ignore caching for local development.
With your settings above, the local shell will always load the latest version of a template file when you run get_template('my_template.html').render()
This is not the default behavior today. Without your settings above, subsequent calls to get_template() will use the first cached version of the file unless you manually invalidate the cache / or restart your shell.
Note: For this answer we will only trace how to configure the settings for the DjangoTemplates template backend, not Jinja2. Where our use case is: we want to entirely avoid template caching for local development.
Let's begin our journey through the docs at the top level settings page to understand the TEMPLATES setting:
TEMPLATES = [ .. ] key"BACKEND" key has no default, and must be defined for each template dict in TEMPLATES = [ {"BACKEND": .. } ].
'django.template.backends.django.DjangoTemplates' or 'django.template.backends.jinja2.Jinja2'settings.TEMPLATES[0]["OPTIONS"] key's values documented either in the DjangoTemplates docs or in the Jinja2 docs, depending on your backend.TEMPLATES[0]["OPTIONS"] under the section that says "DjangoTemplates engines accept the following OPTIONS:"TEMPLATES[0]["OPTIONS"]["loaders"] under the "loaders" bullet.
["loaders"] key while we're still on this page:class cached.Loader
https://docs.djangoproject.com/en/5.1/ref/templates/api/#django.template.loaders.cached.Loader
OPTIONS['loaders'] isn’t specified."
["loaders"] tuple is the loader to use, and the subsequent items remaining in the tuple become arguments passed to it.TEMPLATES[0]["OPTIONS"]["loaders"] will use "django.template.loaders.cached.Loader" as the loader (first item in the tuple) and the list of "django.template.loaders.filesystem.Loader" and "django.template.loaders.app_directories.Loader" become arguments passed into it. (This is explicitly stated in the API | Templates | Configuring an engine | class Engine | loaders section at the top of the page, see my next paragraph).Here's an even more explicit section (still in the same page) that says the cached.Loader is the default:
settings.TEMPLATES dictionary using TEMPLATES[i]["BACKEND"] == DjangoTemplates will be wrapped by an Engine instance.TEMPLATES[0]["OPTIONS"]["loaders"] will be an instance of django.template.loaders.cached.Loader (that first item in the tuple) and it will wrap
'django.template.loaders.filesystem.Loader' and'django.template.loaders.app_directories.Loader' "(if and only if app_dirs is True)"