djangodjango-templatesdjango-cache

Since django 4.1, templates are cached with DEBUG=True. Is this solution right?


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.


Solution

  • Yes, you are correct.
    Yes, that config will work and is well written.

    Here's why.

    Summary

    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

    Why it matters

    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.

    Proving your solution works:
    Django 5 docs walkthrough

    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:

    cached loader is auto enabled if loaders key not specified

    Most succinct place in the Django 5 docs that explain this default behavior

    Here's an even more explicit section (still in the same page) that says the cached.Loader is the default:

    Cached loader default is so difficult to discover or trace