skip to content

Server-Side Templates

The Django template language resolves variables, applies filters and tags, inherits layouts and autoescapes output through pluggable engines. Interviewers probe escaping and custom tags.

part ofDjangooverview, primer and where to startread it →
on this pageshow

explore

questions

27

In Django templates, what does autoescaping do to a {{ review.body }} value, and which characters does it convert?

level: juniorimportance: must knowfreq 60%

answer

  1. on by default for the DTL
  2. five characters, one of them the ampersand
  3. applied after filters, at output
  4. values already marked are skipped
  5. literals and one block tag opt out

basics

~20 s

Django's template engine HTML-escapes every variable's output by default, converting < > ' " and & to entities, after filters run and unless the value is already marked safe, so user text like a product review renders as text, not markup.

solid answer

~40 s

With the default `DjangoTemplates` backend, autoescaping is on: when a `{{ }}` variable is rendered, Django converts `<` to `&lt;`, `>` to `&gt;`, `'` to `&#x27;`, `"` to `&quot;` and `&` to `&amp;`. It happens at output time, after any filters, and it is skipped for values already marked safe (`SafeString`, from `mark_safe`, `format_html` or the `|safe` filter). So a review body containing `<script>` shows up as visible text. `{% autoescape off %}...{% endautoescape %}` disables it for a block, and string literals written in the template itself, such as a `default` filter argument, are never escaped because the template author controls them. Autoescaping is HTML escaping only: it protects element content and quoted attributes, not JavaScript or URL contexts.

code

django · 7 lines
django
{# review.body == '<script>steal()</script> Great blender & quiet' #}
<p>{{ review.body }}</p>
{# output: <p>&lt;script&gt;steal()&lt;/script&gt; Great blender &amp; quiet</p> #}

<p title="{{ review.title }}">{{ review.rating }}/5</p>

{{ review.summary|default:"No summary &mdash; yet" }}  {# literal: not escaped #}

go deeper

for a junior

Recall that Django escapes every {{ }} variable by default and name the five characters it converts, so user text shows as text.

for a middle

Explain that escaping happens at output after filters, that safe strings are skipped, and which exceptions exist: autoescape off, literals and safe values.

for a senior

Show that you know autoescaping is HTML-only and reason about unquoted attributes, href values and script blocks where it does not protect you.

for a principal

Treat every escape hatch as a reviewable exception and set team rules for quoting attributes and handling non-HTML templates rather than disabling escaping.

## What autoescaping is **Autoescaping** is the Django template language's default behaviour of HTML-escaping the output of every variable. On a product-review page, a reviewer can type anything into the review body, including `<script>` or `<img onerror=...>`. If that text were inserted into the page verbatim, the browser would treat it as markup. Autoescaping turns it into harmless text before it reaches the response. It is enabled by default for the `DjangoTemplates` backend (the engine's `autoescape` option defaults to `True`), so a plain template needs no extra work: ```django <article class="review"> <h3>{{ review.title }}</h3> <p>{{ review.body }}</p> </article> ``` ## The five replacements | Character | Becomes | |---|---| | `<` | `&lt;` | | `>` | `&gt;` | | `'` | `&#x27;` | | `"` | `&quot;` | | `&` | `&amp;` | Escaping quotes as well as angle brackets is what lets the same mechanism protect a value placed inside a **quoted** attribute, such as `title="{{ review.title }}"`. ## When it happens 1. The variable is resolved. 2. Any filters in the expression run, in order. 3. At the moment the value is written to the output, Django converts it to a string and escapes it — **unless** the value is already a **safe string**. A safe string is an instance of `SafeString` (a `str` subclass carrying the `SafeData` marker). Values become safe through `mark_safe()`, `format_html()`, the `|safe` filter, or a filter registered as safe that returns a safe result. The check uses the `__html__` convention, so markup objects from other libraries that implement it are also left alone. Because escaping is conditional on that marker, a value is never escaped twice by the automatic pass: `{{ review.body|escape }}` still produces a single round of escaping. ## Ways output is not escaped - **`{% autoescape off %}...{% endautoescape %}`** turns escaping off inside the block. Per Django's documentation, the effect carries into templates that extend the current one and into templates included within the block. `{% autoescape on %}` turns it back on inside an off region. - **String literals in the template** — for example the argument in `{{ review.title|default:"Untitled &amp; unrated" }}` — are inserted without escaping, as if passed through `safe`. The template author controls them, so write entities yourself. - **Safe values** from Python code or filters, as above. - **Engine configuration**: setting the backend's `autoescape` option to `False` disables it for every template the engine renders, which is appropriate only for engines producing non-HTML output such as plain-text emails. ## What autoescaping does not cover Autoescaping is **HTML escaping**. It is the right encoding for text between tags and for quoted attribute values, and it is not a general guarantee: - A value placed unquoted in an attribute can still break out using spaces. - A value placed in an `href` can still be a `javascript:` URL; escaping does not validate URL schemes. - A value placed inside `<script>` needs JavaScript or JSON encoding (`escapejs`, `json_script`), not HTML entities. Those position-specific rules are the general output-encoding model; the Django-specific point is simply that `{{ }}` gives you HTML encoding and nothing more. ## Practical rules - Leave autoescaping on for HTML templates and never disable it to "fix" double-escaped output; find the value that was escaped twice instead. - Keep every attribute value quoted. - Treat each `|safe`, `mark_safe()` and `{% autoescape off %}` as a reviewed exception, because each one removes the protection for that value.

  • Does {{ review.body|escape }} escape the value twice when autoescaping is on?
    No. The `escape` filter returns a value already escaped and marked safe (it uses `conditional_escape`), and the automatic pass skips safe values, so there is one round of escaping. For deliberate repeated escaping Django provides `force_escape`, which escapes immediately every time.
  • A plain-text email template shows &amp; and &#x27; in the message. What is the Django-specific fix?
    Autoescaping is producing HTML entities for a non-HTML format. Wrap the template body in `{% autoescape off %}...{% endautoescape %}`, or render text emails with an engine configured with `autoescape` set to `False`. Never apply that to HTML templates.

saying these in an interview costs you the question

  • Django templates only escape output when you add the |escape filter.
  • Autoescaping strips dangerous tags out of the value.
  • Autoescaping makes a value safe inside a script tag or href.
  • String literals in filter arguments are escaped like variables.
  • Autoescaping runs before filters, so filters see escaped text.
open as a page

In Django's TEMPLATES setting, what do DIRS and APP_DIRS do, and in what order does Django search for a template name?

level: juniorimportance: must knowfreq 58%

basics

~10 s

DIRS lists project template directories searched first, in order; APP_DIRS=True adds each installed app's templates/ subdirectory, searched in INSTALLED_APPS order. Django uses the first file that matches and raises TemplateDoesNotExist if none does.

open as a page

In Django templates, how do you apply, chain and pass an argument to a filter, as in {{ invoice.notes|truncatechars:80 }}?

level: juniorimportance: must knowfreq 58%

basics

~20 s

A filter follows a pipe inside {{ }} and transforms the value before output. Filters chain left to right, each receiving the previous result, and each takes at most one argument after a colon: a literal, a number or a variable.

open as a page

In Django templates, how do {% extends %}, {% block %} and {{ block.super }} work together to build a site-wide page layout?

level: juniorimportance: must knowfreq 72%

basics

~20 s

A child template starts with {% extends 'base.html' %}; each {% block %} it defines replaces the same-named block in the parent, blocks it skips keep the parent's default, and {{ block.super }} inserts the parent block's content instead of discarding it.

open as a page

In Django templates, how does {% for %} work together with {% empty %} and the forloop variable, such as forloop.counter and forloop.last?

level: juniorimportance: must knowfreq 62%

basics

~20 s

{% for item in items %} repeats its body per element; an {% empty %} clause renders instead when the sequence is empty or missing. Inside, forloop exposes counter, counter0, revcounter, revcounter0, first, last, length and parentloop.

open as a page

In the Django template language, what does the dot in {{ student.marks.0 }} try, in what order, and when does it call something?

level: juniorimportance: must knowfreq 62%

basics

~20 s

Each dot tries a dictionary key, then an attribute or method, then a list index, and keeps the first that works; if the value found is callable, Django calls it with no arguments and uses the result.

open as a page

In Django, how would you write a context processor that exposes the current store's branding to every template, and when does it run?

level: middleimportance: must knowfreq 52%

basics

~20 s

Write a function that takes the request and returns a dict such as {'store': ...}, add its dotted path to OPTIONS['context_processors'], and it runs on every render that has a request — render(), render_to_string(..., request=request), generic views — but never on a request-less render.

open as a page

How do you write, register and load a custom Django template filter, such as a money filter for invoice amounts?

level: middleimportance: must knowfreq 50%

basics

~20 s

Put a module in the app's templatetags package, create register = template.Library(), decorate a function with @register.filter, and use {% load module_name %} in the template. The app must be in INSTALLED_APPS, and the dev server needs a restart.

open as a page

When should you write a custom Django template tag instead of a filter, and how do simple_tag and inclusion_tag differ?

level: middleimportance: must knowfreq 48%

basics

~20 s

Write a tag when you need several arguments, the context, a new variable or a rendered fragment; a filter only transforms one value with one argument. simple_tag returns a value; inclusion_tag returns a dict that renders its own template.

open as a page

Why does a Django template render a misspelled {{ studnet.name }} as an empty string, and how do you catch such typos?

level: middleimportance: must knowfreq 55%

basics

~20 s

A failed lookup is swallowed on purpose: Django renders the engine's string_if_invalid option, an empty string by default. To catch typos, temporarily set string_if_invalid to a visible marker, read the django.template DEBUG log, or assert rendered output in tests.

open as a page

In Django templates, when does {{ invoice.discount|default:"none" }} give a wrong answer, and why is default_if_none the fix?

level: juniorimportance: should knowfreq 45%

basics

~10 s

The default filter replaces any falsy value, so a legitimate 0, Decimal("0.00"), empty string or empty list shows the fallback. default_if_none replaces only None, so a real zero discount still renders as 0.00.

open as a page

In a Django template form, what do {% url %} and {% csrf_token %} output, and what happens when either cannot produce its value?

level: juniorimportance: should knowfreq 52%

basics

~20 s

{% url 'name' args %} outputs the path of a named URL pattern and raises NoReverseMatch if reversing fails, unless written with as var. {% csrf_token %} outputs a hidden csrfmiddlewaretoken input, or nothing when the context lacks a token.

open as a page

In Django templates, how should you pass review data from the view to JavaScript, and why is json_script safer than escapejs?

level: middleimportance: should knowfreq 40%

basics

~20 s

Use {{ data|json_script:'review-data' }}, which serializes to JSON, escapes <, > and & and wraps it in a non-executing application/json script tag that JavaScript reads with JSON.parse; escapejs only makes a value safe inside a quoted JavaScript string literal.

open as a page

In Django, why should a helper that builds HTML from user data use format_html() rather than mark_safe() around an f-string?

level: middleimportance: should knowfreq 52%

basics

~10 s

mark_safe() only labels a string as safe without escaping anything, so user data interpolated by an f-string reaches the page raw; format_html() escapes each argument with conditional_escape and then marks the combined result safe.

open as a page

Why does a Django child template that uses {% extends %} silently drop markup placed outside its {% block %} tags?

level: middleimportance: should knowfreq 38%

basics

~10 s

Rendering a child renders its parent; the child contributes only its {% block %} overrides, so text, tags and variables placed outside any block in the child are never output.

open as a page

In Django's {% include %} tag, what context does the included template see, and how do the with and only options change it?

level: middleimportance: should knowfreq 50%

basics

~20 s

A plain {% include %} renders the other template with the full current context; with key=value adds variables for that include only; only restricts it to the with values, dropping view and context-processor variables such as user and request.

open as a page

In Django templates, which operators can {% if %} use, how are and and or grouped without parentheses, and when does {% with %} help?

level: middleimportance: should knowfreq 40%

basics

~20 s

{% if %} supports and, or, not, comparisons, in, not in, is and is not, with filtered operands. Parentheses are invalid and and binds tighter than or, so nest tags for other grouping. {% with %} names an expensive lookup once.

open as a page

In Django templates, when does the engine refuse to call a callable, and what do alters_data and do_not_call_in_templates each change?

level: middleimportance: should knowfreq 38%

basics

~20 s

Django calls any callable it meets with no arguments. alters_data = True blocks the call and renders string_if_invalid, protecting methods like save() and delete(); do_not_call_in_templates = True leaves the object uncalled so its attributes stay reachable.

open as a page

A Django product-review page renders {{ review.body|safe }} so that line breaks display; why is that an XSS hole, and how do you fix it?

level: seniorimportance: should knowfreq 45%

basics

~20 s

The |safe filter marks the reviewer's text as trusted HTML, so any script or event-handler markup they typed runs for every visitor; remove it and use |linebreaks, which escapes the text before adding <p> and <br>, or sanitize real rich text first.

open as a page

In Django 6.1, when is the cached template loader active, and what changes if you set OPTIONS['loaders'] in TEMPLATES yourself?

level: seniorimportance: should knowfreq 38%

basics

~20 s

With DjangoTemplates and no OPTIONS['loaders'], Django wraps its filesystem and app-directories loaders in cached.Loader in every environment; setting loaders yourself replaces that default, forbids APP_DIRS, and caches only if you wrap the list in the cached loader.

open as a page

When writing a custom Django template filter, what do the is_safe, needs_autoescape and expects_localtime flags do, and when do you need each?

level: seniorimportance: should knowfreq 28%

basics

~20 s

is_safe=True keeps a safe input's result marked safe, for filters that add no HTML characters. needs_autoescape=True passes the current autoescape mode so a filter that builds HTML can escape its inputs. expects_localtime=True converts aware datetimes to the current time zone first.

open as a page

A Django inclusion tag's template cannot see request.user even though the page can; why, and how should takes_context be used to fix it?

level: seniorimportance: should knowfreq 26%

basics

~10 s

An inclusion tag renders its template with a fresh context holding only the dictionary its function returns, plus csrf_token. Use takes_context=True, name the first parameter context, and return the values the fragment needs.

open as a page

A Django gradebook template loops {% for subject, marks in scores.items %} over a defaultdict and renders nothing; why, and how do you fix it?

level: seniorimportance: should knowfreq 30%

basics

~20 s

Dictionary lookup runs before attribute lookup, so scores.items becomes scores["items"]; a defaultdict creates that key and returns an empty list, so the loop iterates nothing. Convert to a plain dict, or pass pre-built pairs, in the view.

open as a page

In a Django project, what changes when you configure the Jinja2 template backend alongside or instead of DjangoTemplates?

level: middleimportance: nice to knowfreq 25%

basics

~20 s

The Jinja2 backend loads templates from each app's jinja2/ folder, knows none of Django's tags or filters, adds only request, csrf_input and csrf_token globals, and is customised through an environment callable; the admin still requires a DjangoTemplates engine.

open as a page

In Django 5.1 and later, what does the {% querystring %} tag do for pagination links that must keep the current filters?

level: middleimportance: nice to knowfreq 24%

basics

~20 s

{% querystring %} rebuilds the query string from request.GET with changes: keyword arguments add or replace keys and None removes one, so page links keep active filters. Since Django 6.0 the output always starts with ?.

open as a page

In Django 6.0+, how would you use {% partialdef %} and the template_name#partial_name syntax to re-render one booking row without duplicating its markup?

level: seniorimportance: nice to knowfreq 28%

basics

~20 s

Wrap the row in {% partialdef booking-row inline %} in the list template, loop over it with {% partial booking-row %} or inline, and have the update view call render(request, 'bookings/list.html#booking-row', {'booking': booking}) to return only that fragment.

open as a page

In a Django template, why does {{ student.profile.advisor }} render blank when the profile is missing while a bug inside a property crashes the page?

level: seniorimportance: nice to knowfreq 22%

basics

~20 s

An exception raised by code a lookup runs, such as a property or method, propagates unless its class sets silent_variable_failure = True. ObjectDoesNotExist sets it, so a missing related object renders string_if_invalid; an ordinary AttributeError or ZeroDivisionError inside a property or method stops the render.

open as a page