{"id":1753,"date":"2026-09-27T06:12:32","date_gmt":"2026-09-27T06:12:32","guid":{"rendered":"https:\/\/nayananimsara.online\/?p=1753"},"modified":"2026-09-27T06:14:31","modified_gmt":"2026-09-27T06:14:31","slug":"django-template-inheritance-explained-pt2","status":"publish","type":"post","link":"https:\/\/nayananimsara.online\/django-template-inheritance-explained-pt2\/","title":{"rendered":"Django Template Inheritance Explained: DTL Part 2"},"content":{"rendered":"\n<h1 id=\"django-template-inheritance-explained-dtl-part-2\" class=\"wp-block-heading\">Django Template Inheritance Explained: DTL Part 2<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Django Template Inheritance Explained, Part 1 solved one problem and left another sitting there on purpose. Your homepage now pulls in <code>{{ site_name }}<\/code> and <code>{{ tagline }}<\/code> from a real template file instead of a Python string \u2014 but that template still has its own full <code>&lt;!DOCTYPE html><\/code>, its own <code>&lt;head><\/code>, its own navbar. Build a second page with <code>render()<\/code> the same way, and you&#8217;re copying that exact structure into a second file. Build a fifth page, and you&#8217;re maintaining five copies of the same navbar.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Template inheritance is what actually fixes that, and it&#8217;s the whole subject of this post.<\/p>\n\n\n\n<div class=\"wp-block-rank-math-toc-block\" id=\"rank-math-toc\"><h2>Table of Contents<\/h2><nav><ul><li><a href=\"#django-template-inheritance-explained-dtl-part-2\">Django Template Inheritance Explained: DTL Part 2<\/a><ul><li><a href=\"#the-idea-behind-inheritance-django-template-inheritance-explained\">The Idea Behind Inheritance (Django Template Inheritance Explained)<\/a><\/li><li><a href=\"#step-1-create-a-project-level-templates-folder\">Step 1: Create a Project-Level Templates Folder<\/a><\/li><li><a href=\"#step-2-rewrite-home-html-to-extend-it\">Step 2: Rewrite home.html to Extend It<\/a><\/li><li><a href=\"#step-3-give-the-about-page-the-same-treatment\">Step 3: Give the About Page the Same Treatment<\/a><\/li><li><a href=\"#proving-the-point-change-the-navbar-once\">Proving the Point: Change the Navbar Once<\/a><\/li><li><a href=\"#whats-deliberately-left-out-here\">What&#8217;s Deliberately Left Out Here<\/a><\/li><li><a href=\"#mistakes-worth-flagging\">Mistakes Worth Flagging<\/a><\/li><li><a href=\"#series-connections\">Series Connections<\/a><\/li><\/ul><\/li><\/ul><\/nav><\/div>\n\n\n\n<h2 id=\"the-idea-behind-inheritance-django-template-inheritance-explained\" class=\"wp-block-heading\">The Idea Behind Inheritance (Django Template Inheritance Explained)<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">One template holds the shared structure \u2014 the <code>&lt;head><\/code>, the navbar, the footer. Every other page template extends that one, filling in only the part that&#8217;s actually different for that page. Change the navbar once, in one file, and every page using it updates automatically.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Two tags make this work: <code>{% extends %}<\/code> and <code>{% block %}<\/code>.<\/p>\n\n\n\n<h2 id=\"step-1-create-a-project-level-templates-folder\" class=\"wp-block-heading\">Step 1: Create a Project-Level Templates Folder<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Up to now, templates have lived inside <code>pages\/templates\/<\/code>, scoped to one app. A base template is meant to be shared across every app in the project, so it doesn&#8217;t really belong inside any single app&#8217;s folder. The standard place for it is a <code>templates<\/code> folder at the project root, sitting next to <code>manage.py<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>myfirstproject\/\n    manage.py\n    templates\/\n        base.html\n    pages\/\n        templates\/\n            pages\/\n                home.html\n    myfirstproject\/\n        settings.py<\/code><\/pre>\n\n\n\n<figure class=\"wp-block-image aligncenter size-full\"><img fetchpriority=\"high\" decoding=\"async\" width=\"285\" height=\"524\" src=\"https:\/\/nayananimsara.online\/wp-content\/uploads\/2026\/09\/image-27.png\" alt=\"Django Template Inheritance Explained\" class=\"wp-image-1754\" srcset=\"https:\/\/nayananimsara.online\/wp-content\/uploads\/2026\/09\/image-27.png 285w, https:\/\/nayananimsara.online\/wp-content\/uploads\/2026\/09\/image-27-163x300.png 163w\" sizes=\"(max-width: 285px) 100vw, 285px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Django doesn&#8217;t search this root-level folder automatically \u2014 you have to point it there yourself. Open <code>settings.py<\/code>, find the <code>TEMPLATES<\/code> setting, and update the <code>DIRS<\/code> list:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">python<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>TEMPLATES = &#91;\n    {\n        'BACKEND': 'django.template.backends.django.DjangoTemplates',\n        'DIRS': &#91;BASE_DIR \/ 'templates'],\n        'APP_DIRS': True,\n        ...\n    },\n]<\/code><\/pre>\n\n\n\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" width=\"878\" height=\"434\" src=\"https:\/\/nayananimsara.online\/wp-content\/uploads\/2026\/09\/image-28.png\" alt=\"Django Template Inheritance Explained\" class=\"wp-image-1755\" srcset=\"https:\/\/nayananimsara.online\/wp-content\/uploads\/2026\/09\/image-28.png 878w, https:\/\/nayananimsara.online\/wp-content\/uploads\/2026\/09\/image-28-300x148.png 300w, https:\/\/nayananimsara.online\/wp-content\/uploads\/2026\/09\/image-28-767x379.png 767w\" sizes=\"(max-width: 878px) 100vw, 878px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\"><code>BASE_DIR<\/code> is already defined near the top of <code>settings.py<\/code> by default \u2014 it just points to your project&#8217;s root folder, the same place <code>manage.py<\/code> lives. Adding <code>BASE_DIR \/ 'templates'<\/code> to <code>DIRS<\/code> tells Django to also look inside that root <code>templates<\/code> folder, in addition to each app&#8217;s own <code>templates<\/code> folder through <code>APP_DIRS<\/code>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Now create <code>base.html<\/code> inside that root folder, not inside <code>pages\/templates\/<\/code>. Put everything that should appear on every page:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Also Add App In To Installed Apps<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" width=\"472\" height=\"295\" src=\"https:\/\/nayananimsara.online\/wp-content\/uploads\/2026\/09\/image-32.png\" alt=\"Django Template Inheritance Explained\" class=\"wp-image-1759\" srcset=\"https:\/\/nayananimsara.online\/wp-content\/uploads\/2026\/09\/image-32.png 472w, https:\/\/nayananimsara.online\/wp-content\/uploads\/2026\/09\/image-32-300x188.png 300w, https:\/\/nayananimsara.online\/wp-content\/uploads\/2026\/09\/image-32-360x224.png 360w\" sizes=\"(max-width: 472px) 100vw, 472px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">html<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;!DOCTYPE html&gt;\n&lt;html lang=\"en\"&gt;\n&lt;head&gt;\n    &lt;meta charset=\"UTF-8\"&gt;\n    &lt;title&gt;{% block title %}My Django Site{% endblock %}&lt;\/title&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n    &lt;nav&gt;\n        &lt;a href=\"\/\"&gt;Home&lt;\/a&gt;\n        &lt;a href=\"\/about\/\"&gt;About&lt;\/a&gt;\n    &lt;\/nav&gt;\n\n    &lt;main&gt;\n        {% block content %}{% endblock %}\n    &lt;\/main&gt;\n\n    &lt;footer&gt;\n        &lt;p&gt;&amp;copy; 2026 My Django Site&lt;\/p&gt;\n    &lt;\/footer&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;<\/code><\/pre>\n\n\n\n<figure class=\"wp-block-image aligncenter size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"768\" src=\"https:\/\/nayananimsara.online\/wp-content\/uploads\/2026\/09\/image-29-1024x768.png\" alt=\"Django Template Inheritance Explained\" class=\"wp-image-1756\" srcset=\"https:\/\/nayananimsara.online\/wp-content\/uploads\/2026\/09\/image-29-1024x768.png 1024w, https:\/\/nayananimsara.online\/wp-content\/uploads\/2026\/09\/image-29-300x225.png 300w, https:\/\/nayananimsara.online\/wp-content\/uploads\/2026\/09\/image-29-768x576.png 768w, https:\/\/nayananimsara.online\/wp-content\/uploads\/2026\/09\/image-29.png 1114w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Those <code>{% block %}<\/code> tags aren&#8217;t logic \u2014 they&#8217;re not <code>{% if %}<\/code>, they&#8217;re not checking anything. They&#8217;re just named placeholders, marking the spots where a child template is allowed to insert its own content.<\/p>\n\n\n\n<h2 id=\"step-2-rewrite-home-html-to-extend-it\" class=\"wp-block-heading\">Step 2: Rewrite home.html to Extend It<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Instead of a full HTML document, <code>home.html<\/code> now only needs to declare what it extends and fill in the blocks:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">html<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>{% extends \"base.html\" %}\n\n{% block title %}Home{% endblock %}\n\n{% block content %}\n    &lt;h1&gt;Welcome to {{ site_name }}&lt;\/h1&gt;\n    &lt;p&gt;{{ tagline }}&lt;\/p&gt;\n{% endblock %}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><code>{% extends \"base.html\" %}<\/code> has to be the very first line in the file \u2014 Django requires that. Everything below it fills in the named blocks from the base template. The view from Part 1 doesn&#8217;t need to change at all; it still passes <code>site_name<\/code> and <code>tagline<\/code> through <code>render()<\/code> exactly as before.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Reload the homepage. Visually, nothing looks different. Structurally, the entire page is now built from two files working together instead of one file repeating everything.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"501\" src=\"https:\/\/nayananimsara.online\/wp-content\/uploads\/2026\/09\/image-30-1024x501.png\" alt=\"Django Template Inheritance Explained\" class=\"wp-image-1757\" srcset=\"https:\/\/nayananimsara.online\/wp-content\/uploads\/2026\/09\/image-30-1024x501.png 1024w, https:\/\/nayananimsara.online\/wp-content\/uploads\/2026\/09\/image-30-766x375.png 766w, https:\/\/nayananimsara.online\/wp-content\/uploads\/2026\/09\/image-30-300x147.png 300w, https:\/\/nayananimsara.online\/wp-content\/uploads\/2026\/09\/image-30.png 1218w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<h2 id=\"step-3-give-the-about-page-the-same-treatment\" class=\"wp-block-heading\">Step 3: Give the About Page the Same Treatment<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Assuming you have an <code>about.html<\/code> template already, or are creating one now, the pattern is identical:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">html<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>{% extends \"base.html\" %}\n\n{% block title %}About{% endblock %}\n\n{% block content %}\n    &lt;h1&gt;About This Site&lt;\/h1&gt;\n    &lt;p&gt;This page shares the same navbar and footer as the homepage, without repeating any of that HTML here.&lt;\/p&gt;\n{% endblock %}<\/code><\/pre>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"395\" src=\"https:\/\/nayananimsara.online\/wp-content\/uploads\/2026\/09\/image-31-1024x395.png\" alt=\"Django Template Inheritance Explained\" class=\"wp-image-1758\" srcset=\"https:\/\/nayananimsara.online\/wp-content\/uploads\/2026\/09\/image-31-1024x395.png 1024w, https:\/\/nayananimsara.online\/wp-content\/uploads\/2026\/09\/image-31-767x296.png 767w, https:\/\/nayananimsara.online\/wp-content\/uploads\/2026\/09\/image-31-300x116.png 300w, https:\/\/nayananimsara.online\/wp-content\/uploads\/2026\/09\/image-31.png 1138w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Visit <code>\/about\/<\/code>, and the navbar, the <code>&lt;head&gt;<\/code>, the footer \u2014 all identical to the homepage, without a single line of that markup existing in this file.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">[Screenshot spot: home and about pages open in two tabs, both showing the identical navbar, proving they&#8217;re sharing one base file.]<\/p>\n\n\n\n<h2 id=\"proving-the-point-change-the-navbar-once\" class=\"wp-block-heading\">Proving the Point: Change the Navbar Once<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">This is the part worth actually testing, not just reading about. Open <code>base.html<\/code> and add a third link:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">html<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;nav&gt;\n    &lt;a href=\"\/\"&gt;Home&lt;\/a&gt;\n    &lt;a href=\"\/about\/\"&gt;About&lt;\/a&gt;\n    &lt;a href=\"\/contact\/\"&gt;Contact&lt;\/a&gt;\n&lt;\/nav&gt;<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Reload both the homepage and the About page. The new link appears on both, instantly, without touching <code>home.html<\/code> or <code>about.html<\/code> at all. That single edit updating every page at once is the entire reason inheritance exists.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"438\" src=\"https:\/\/nayananimsara.online\/wp-content\/uploads\/2026\/09\/image-33-1024x438.png\" alt=\"Django Template Inheritance Explained\" class=\"wp-image-1760\" srcset=\"https:\/\/nayananimsara.online\/wp-content\/uploads\/2026\/09\/image-33-1024x438.png 1024w, https:\/\/nayananimsara.online\/wp-content\/uploads\/2026\/09\/image-33-767x328.png 767w, https:\/\/nayananimsara.online\/wp-content\/uploads\/2026\/09\/image-33-300x128.png 300w, https:\/\/nayananimsara.online\/wp-content\/uploads\/2026\/09\/image-33.png 1111w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<h2 id=\"whats-deliberately-left-out-here\" class=\"wp-block-heading\">What&#8217;s Deliberately Left Out Here<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Consistent with where this course actually is, this post doesn&#8217;t touch <code>{% if %}<\/code> or <code>{% for %}<\/code> \u2014 those get their own dedicated lesson separately. There&#8217;s no CSS or static file loading yet either. <code>{% extends %}<\/code> and <code>{% block %}<\/code> are the only two new pieces of syntax in this post, kept isolated so the mechanics of inheritance don&#8217;t get tangled up with anything else.<\/p>\n\n\n\n<h2 id=\"mistakes-worth-flagging\" class=\"wp-block-heading\">Mistakes Worth Flagging<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Two things trip people up at this stage. First, forgetting <code>{% extends \"base.html\" %}<\/code> as the literal first line of a child template \u2014 even a comment or blank line above it causes an error. Second, and specific to this setup, forgetting to add <code>BASE_DIR \/ 'templates'<\/code> to <code>DIRS<\/code> in <code>settings.py<\/code>. Without that line, Django has no idea the root-level <code>templates<\/code> folder exists, and you&#8217;ll get a &#8220;TemplateDoesNotExist&#8221; error even though the file is sitting right there. If <code>base.html<\/code> won&#8217;t resolve after moving it, that setting is the first thing worth checking.<\/p>\n\n\n\n<h2 id=\"series-connections\" class=\"wp-block-heading\">Series Connections<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">This continues directly from Part 1 of this series, where <code>render()<\/code> and <code>{{ variable }}<\/code> display were introduced \u2014 worth revisiting if the context dictionary or <code>render()<\/code> call here feels unfamiliar. For a deeper look at overriding blocks selectively, including calling a parent block&#8217;s original content with <code>{{ block.super }}<\/code>, the <a href=\"https:\/\/docs.djangoproject.com\/en\/stable\/ref\/templates\/language\/#template-inheritance\" target=\"_blank\" rel=\"noopener\">official Django documentation on template inheritance<\/a> covers patterns beyond what&#8217;s needed here.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Convert one more page of your own to extend <code>base.html<\/code> before the next lesson, then make one small edit to the base file and watch it apply everywhere at once. That&#8217;s the moment inheritance stops being a concept and starts being obviously useful.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/nayananimsara.online\/course\/django-course-sinhala\/\" target=\"_blank\" data-type=\"link\" data-id=\"https:\/\/nayananimsara.online\/course\/django-course-sinhala\/\" rel=\"noreferrer noopener\">Full Course<\/a><\/p>\n\n\n\n<figure class=\"wp-block-embed is-type-video is-provider-youtube wp-block-embed-youtube wp-embed-aspect-16-9 wp-has-aspect-ratio\"><div class=\"wp-block-embed__wrapper\">\n<div class=\"tg-oembed-container\"><iframe title=\"WebHD 720p 15\" width=\"640\" height=\"360\" src=\"https:\/\/www.youtube.com\/embed\/4EmjobuTPEI?feature=oembed\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" referrerpolicy=\"strict-origin-when-cross-origin\" allowfullscreen><\/iframe><\/div>\n<\/div><\/figure>\n","protected":false},"excerpt":{"rendered":"<p>Django Template Inheritance Explained: DTL Part 2 Django Template Inheritance Explained, Part 1 solved one problem and left another sitting there on purpose. Your homepage now pulls in {{ site_name }} and {{ tagline }} from a real template file instead of a Python string \u2014 but that template still has its own full &lt;!DOCTYPE [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"elearning_container_layout":"tg-site-layout--default","elearning_sidebar_layout":"tg-site-layout--default","elearning_remove_content_margin":false,"elearning_sidebar":"default","elearning_transparent_header":"customizer","elearning_logo":0,"elearning_header_style":"default","elearning_menu_item_color":"","elearning_menu_item_hover_color":"","elearning_menu_item_active_color":"","elearning_menu_item_active_style":"","elearning_page_header":true,"footnotes":""},"categories":[64],"tags":[],"class_list":["post-1753","post","type-post","status-publish","format-standard","hentry","category-django"],"_links":{"self":[{"href":"https:\/\/nayananimsara.online\/wp-json\/wp\/v2\/posts\/1753","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/nayananimsara.online\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/nayananimsara.online\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/nayananimsara.online\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/nayananimsara.online\/wp-json\/wp\/v2\/comments?post=1753"}],"version-history":[{"count":3,"href":"https:\/\/nayananimsara.online\/wp-json\/wp\/v2\/posts\/1753\/revisions"}],"predecessor-version":[{"id":1763,"href":"https:\/\/nayananimsara.online\/wp-json\/wp\/v2\/posts\/1753\/revisions\/1763"}],"wp:attachment":[{"href":"https:\/\/nayananimsara.online\/wp-json\/wp\/v2\/media?parent=1753"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/nayananimsara.online\/wp-json\/wp\/v2\/categories?post=1753"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/nayananimsara.online\/wp-json\/wp\/v2\/tags?post=1753"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}