In Django templates, how do {% extends %}, {% block %} and {{ block.super }} work together to build a site-wide page layout?
answer
- parent declares, child replaces
- named regions with default content
- first tag in the child
- unfilled regions keep the default
- add to the parent's content
basics
~20 sA 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.
solid answer
~40 sA base template such as `base.html` lays out the whole page and marks replaceable regions with `{% block name %}...{% endblock %}`; whatever sits inside a block is its default. A child template begins with `{% extends 'base.html' %}` — it must be the first template tag — and defines blocks with the same names. At render time Django renders the *parent*, substituting the child's version of each block it overrides and keeping the parent's default for every block it doesn't. Inside an overriding block, `{{ block.super }}` renders the next level up's content for that block, so a page can add to it (an extra stylesheet, a title suffix) instead of copying it. Chains work too: a page can extend `bookings/base_bookings.html`, which extends `base.html`, and the most-derived override of each block wins.
code
django · 13 lines{# templates/bookings/base_bookings.html #}
{% extends 'base.html' %}
{% block nav %}
{{ block.super }}
<a href="/bookings/upcoming/">Upcoming</a>
<a href="/bookings/past/">Past</a>
{% endblock nav %}
{% block extra_head %}
{{ block.super }}
<link rel="stylesheet" href="/static/css/bookings.css">
{% endblock %}go deeper
Recall the three pieces: extends names the parent and comes first, block marks a replaceable region with a default, and block.super keeps the parent's content inside your override.
Explain that the parent is what actually renders, that unoverridden blocks keep defaults, and how block.super climbs one level at a time in a three-level site/section/page chain.
Show how you would design a base layout with enough hooks (extra_head, scripts, nav) that pages never copy markup, and how a variable extends lets one view pick a layout.
Weigh deep inheritance chains against flatter layouts plus fragments: each extra level adds indirection a reader must trace, so keep chains shallow and hooks purposeful.
## What template inheritance solves A travel-bookings site has dozens of pages — search results, a booking list, a booking detail, checkout — and every one needs the same `<head>`, navigation bar, footer and script tags. Copying that skeleton into each file means one navigation change touches every page. **Template inheritance** in the Django template language (DTL) solves this with two tags and one variable: - **`{% block name %}...{% endblock %}`** marks a named, replaceable region. Whatever sits between the tags is the block's **default content**. - **`{% extends 'base.html' %}`** declares that the current template is a **child** of another template, its **parent**. - **`{{ block.super }}`**, used inside an overriding block, renders the parent's version of that block. ## The parent: a skeleton with named holes ```django {# templates/base.html #} <!DOCTYPE html> <html lang="en"> <head> <title>{% block title %}Trips{% endblock %}</title> {% block extra_head %}{% endblock %} </head> <body> <nav>{% block nav %}<a href="/">Home</a> <a href="/bookings/">My trips</a>{% endblock %}</nav> <main>{% block content %}{% endblock %}</main> </body> </html> ``` The parent is an ordinary template: rendered on its own it outputs its defaults. The `block` tags only announce that a child *may* replace those regions. ## The child: override only what differs ```django {# templates/bookings/list.html #} {% extends 'base.html' %} {% block title %}{{ block.super }} · My bookings{% endblock %} {% block content %} <h1>Upcoming trips</h1> ... {% endblock content %} ``` When Django renders `bookings/list.html`, it sees the `extends` tag, loads the parent, and then renders the **parent** — substituting the child's version of every block the child defines. Here the title becomes `Trips · My bookings`, `content` is replaced, and `extra_head` and `nav` fall back to the parent's defaults because the child never mentions them. A child does **not** have to override every block; Django's own documentation recommends many blocks in the base, because each one is a cheap hook that costs nothing when unused. The closing tag may repeat the name (`{% endblock content %}`) purely for readability. ## `{{ block.super }}`: extend instead of replace Overriding a block replaces its contents entirely. When a page wants to *add* to the parent's content — one more stylesheet in `extra_head`, a suffix on the title, an extra link in `nav` — it writes `{{ block.super }}` where the parent's content should appear. Two details matter: 1. It renders the **next level up** in the chain, not necessarily `base.html`. In a three-level chain, the page's `block.super` returns the section template's override if the section defines that block, and the base's default otherwise. 2. Its output is **marked safe**, so it is not escaped a second time; the parent's variables were already escaped when that block rendered. ## Multi-level inheritance The Django docs describe a common three-level layout: | Level | Example file | Holds | |---|---|---| | Site | `base.html` | `<head>`, global nav, footer | | Section | `bookings/base_bookings.html` | section sub-navigation, section CSS | | Page | `bookings/list.html` | the page's own content | `base_bookings.html` extends `base.html` and overrides `nav` with `{{ block.super }}` plus a bookings sub-menu; each bookings page extends `base_bookings.html`. For every block the most-derived override wins, and each `block.super` walks one level up. ## Rules that keep inheritance working - `{% extends %}` must be the **first template tag** in the child; any other tag before it, even `{% load %}`, raises `TemplateSyntaxError`. - A template may contain **one** `{% extends %}` and may not define two blocks with the same name — both are `TemplateSyntaxError`s at parse time. - `{% extends %}` accepts a quoted name, a relative path such as `'./base_bookings.html'`, or a variable holding either a template name or a compiled `Template` — useful when the layout is chosen per request. - The parent's name is resolved by the configured template loaders like any other template name. The mental model to keep: the child does not wrap the parent. The parent is what gets rendered, and the child is a set of named replacements for its blocks.
- In a three-level chain, what does {{ block.super }} in the page template return?The next level up: the section template's version of that block if the section overrides it (which may itself call `{{ block.super }}`), otherwise the base template's default. Each `block.super` climbs exactly one level, so contributions from every level can be stacked.
- Can the parent of {% extends %} be chosen at runtime?Yes. `{% extends layout %}` takes a variable; if it resolves to a string Django loads that template name, and if it resolves to a compiled `Template` it uses that object directly. An empty value raises `TemplateSyntaxError`, so the view must always supply a valid layout.
A base layout is a printed form with labelled boxes that already hold sample text; a child page is a sheet of sticky notes, one per box it wants to change. Boxes without a note keep the printed text, and {{ block.super }} is copying the printed text onto your note before adding a line.
saying these in an interview costs you the question
- A child template must override every block its parent declares.
- {% extends %} can appear anywhere in the child, for example after {% load %}.
- Overriding a block automatically appends to the parent's content.
- {{ block.super }} always jumps straight to base.html, skipping middle levels.
- The child template is rendered first and the parent is wrapped around it.