Why does a Django project's override of django/forms/field.html in its template DIRS get ignored, and what does the FORM_RENDERER setting change?
answer
- forms have their own template engine
- the default ignores TEMPLATES
- a renderer that uses TEMPLATES
- django.forms in INSTALLED_APPS
- it reaches admin forms too
basics
~20 sDjango's default form renderer, DjangoTemplates, uses its own engine that loads the built-in form templates and apps' templates folders, not the project's TEMPLATES DIRS. Set FORM_RENDERER to TemplatesSetting, or a subclass, to make overrides work.
solid answer
~40 s`FORM_RENDERER` names the class that renders form, formset, field, label, error and widget templates; it defaults to `"django.forms.renderers.DjangoTemplates"`. That renderer builds a **standalone** Django template engine that searches Django's built-in form templates directory first and then installed apps' `templates/` directories — it never reads the project's `TEMPLATES` setting. So `templates/django/forms/field.html` in the project `DIRS` is never found, and even an app template with the same path loses to the built-in one. Switching to `django.forms.renderers.TemplatesSetting` makes form rendering use the project's configured engines; the built-in templates must then still be findable, usually by adding `"django.forms"` to `INSTALLED_APPS` with `APP_DIRS=True`. A renderer subclass can also set `form_template_name`, `formset_template_name`, `field_template_name` and, since 5.2, `bound_field_class` — for every form using the default renderer, admin included.
code
python · 21 linesTEMPLATES = [
{
"BACKEND": "django.template.backends.django.DjangoTemplates",
"DIRS": [BASE_DIR / "templates"],
"APP_DIRS": True,
"OPTIONS": {},
},
]
INSTALLED_APPS = [
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
"django.forms",
"donations",
]
FORM_RENDERER = "django.forms.renderers.TemplatesSetting"go deeper
Remember that form HTML comes from templates rendered by a form renderer named in FORM_RENDERER, and that the default one does not read your TEMPLATES setting.
Explain the search paths of DjangoTemplates, Jinja2 and TemplatesSetting, why a DIRS override is ignored, and the django.forms plus APP_DIRS requirement.
Treat a renderer switch as a site-wide change: check admin and third-party forms, remove removed transitional renderers from settings, and keep per-form exceptions explicit.
Decide whether the project's form markup is owned centrally through a renderer or left to pages, and how that choice interacts with third-party apps you do not control.
## What a form renderer is Every piece of Django form HTML — the form, a formset, a field group, a label, an error list and each widget — is produced by rendering a template. The object that finds and renders those templates is the **form renderer**. `FORM_RENDERER` in settings names its class and defaults to `"django.forms.renderers.DjangoTemplates"`. A form gets its renderer from, in order, the `renderer=` argument, the form class's `default_renderer`, or the project default built from `FORM_RENDERER`. ## Why the override is ignored The built-in renderers differ in **where they look for templates**: | renderer | engine | searches | |---|---|---| | `DjangoTemplates` (default) | a standalone Django template engine | Django's built-in form templates, then each installed app's `templates/` | | `Jinja2` | a standalone Jinja2 engine | Django's built-in Jinja2 form templates, then each app's `jinja2/` | | `TemplatesSetting` | the engines in your `TEMPLATES` setting | wherever those engines look | The default engine is unconnected to `TEMPLATES`. That explains the usual symptom: - A file at `<project>/templates/django/forms/field.html`, listed in `TEMPLATES["DIRS"]`, is simply not on the default renderer's search path. - A file at the same relative path inside an app's `templates/` directory is on the path, but the built-in directory is searched first, so the built-in `field.html` still wins. - A template with a **new** name inside an app's `templates/` directory — say `donations/forms/field.html` — is found, because nothing earlier on the path shadows it. Django's documentation is explicit: to override built-in form, formset, field or widget templates, use the `TemplatesSetting` renderer. ## Switching to TemplatesSetting ```python # settings.py INSTALLED_APPS = [ # ... "django.forms", ] FORM_RENDERER = "django.forms.renderers.TemplatesSetting" ``` `TemplatesSetting` calls `django.template.loader.get_template()`, so form templates resolve exactly like page templates: project `DIRS` first if your engine lists them, then apps. Because it no longer adds Django's own form templates automatically, the built-in templates must be reachable, by either: 1. adding `"django.forms"` to `INSTALLED_APPS` with at least one engine using `APP_DIRS=True`, or 2. adding Django's `forms/templates` directory to an engine's `DIRS`. Forget both and the first form on the site fails with `TemplateDoesNotExist` for a built-in template such as `django/forms/div.html`. ## Customising more than the search path A renderer subclass is also the one place to set project-wide defaults: - `form_template_name` — what `{{ form }}` renders (default `django/forms/div.html`); - `formset_template_name` — what `{{ formset }}` renders (default `django/forms/formsets/div.html`); - `field_template_name` — what `as_field_group()` renders (default `django/forms/field.html`); - `bound_field_class` — the `BoundField` class used for every field (Django 5.2+). ```python from django.forms.renderers import TemplatesSetting class DonationFormRenderer(TemplatesSetting): form_template_name = "donations/forms/form.html" field_template_name = "donations/forms/field.html" ``` ## Consequences to plan for - **It is global.** The project default renderer is used by the admin and by third-party apps' forms too. A new `form_template_name` or an overridden widget template changes their output as well, so review those pages after switching. - **Per-form exceptions stay possible.** Set `default_renderer` on a form class, or pass `renderer=` when instantiating, to render one form differently. - **Jinja2 is all-or-nothing.** The `Jinja2` renderer needs Jinja2 templates for every form and widget in the project; the admin's widgets have none, so it is rarely a drop-in choice. - **Removed transitional classes.** `DjangoDivFormRenderer` and `Jinja2DivFormRenderer`, added in 4.1 to opt into div output early, were deprecated in 5.0 and removed in 6.0; a settings file that still names them must drop the line. - **Tests.** The default renderer is cached, but Django clears that cache when `FORM_RENDERER` changes through `override_settings`, so tests can switch renderers safely. ## Choosing a renderer - Keep **`DjangoTemplates`** when you only add new template names inside an app and never replace built-in ones; it needs no extra settings. - Use **`TemplatesSetting`** (or a subclass) as soon as you override a built-in form, field or widget template, or want form templates to live beside the rest of the project's templates. - Use **`Jinja2`** only when every form and widget the project renders has a Jinja2 template, which rules it out wherever the admin's widgets are used.
- What error appears if you switch to TemplatesSetting but forget django.forms?Unless Django's form templates directory is in some engine's `DIRS`, rendering the first form raises `TemplateDoesNotExist` for a built-in template such as `django/forms/div.html` or a widget template, because `TemplatesSetting` searches only what the `TEMPLATES` engines can find.
- How can one form keep the default renderer after the project switches?Set `default_renderer` on that form class to a renderer class or instance, or pass `renderer=` when instantiating it. Both take precedence over the project default built from `FORM_RENDERER`.
The default form renderer is a print shop with its own shelf of stencils: it checks Django's stencils first and then each app's drawer, but never the project's filing cabinet. TemplatesSetting sends the job to the office's main filing system, where your version of a stencil is found first.
saying these in an interview costs you the question
- form templates are always loaded through the TEMPLATES setting
- an app template at django/forms/field.html overrides the built-in under the default renderer
- TemplatesSetting finds Django's built-in form templates with no extra setup
- changing FORM_RENDERER affects only the project's own forms, not the admin
- DjangoDivFormRenderer is still needed to get div output in Django 6.x