Raghavendra Kumar
Raghavendra Kumar

Reputation: 728

Jinja2 Multi-level template inheritance issue

I have the following three files in a Django App, with the template engine being Jinja2

skeleton.html

<head>
    {% block head_content %}
        <meta charset="utf-8">
        <title> {% if page_title %} {{ page_title }} | {% endif %} Bhargavi Books & Stationery </title>
        <link rel="stylesheet" href="{{ static("css/semantic.min.css") }}">
        <link rel="stylesheet" href="{{ static("Icons/font-awesome.min.css") }}">
    {% endblock head_content %}
</head>

<body>
    <div id="navigation">
    {% block navigation %}
        <div class="ui three item menu">
          <a class="active item">Home</a>
          <a class="item">New Bill</a>
          <a class="item">View Bills</a>
        </div>
    {% endblock navigation %}
    </div>

    <div id="frame">
        {% block frame_content %}
            <p> Body content goes here. Body for {{ content_name }}</p>
        {% endblock frame_content %}
    </div>

    <div id="app">
        {% block app_content %}
            <p> APP content goes here. Body for {{ content_name }}</p>
        {% endblock app_content %}
    </div>

    {% block scripts %}
        <script src="{{ static("js/jquery.js") }}"></script>
        <script src=" {{ static("js/semantic.min.js") }} "></script>
    {% endblock scripts %}
</body>

base.html

{% extends "skeleton.html" %}
{% from "macros/globalmacros.html" import
            SUIIconList,SUISimpleList,
            SUIImageLabel,SUILabel,
            SUIActionInput,SUILabeledInput,SUIInput,
            SUIDimmableActionCard,SUICard,
 %}


{% block frame_content %}
Frame Content
{% endblock frame_content %}

{% block scripts %}

{{ super() }}
<script src=" {{ static("js/globalmacros.js") }} "></script>

{% endblock scripts %}

dashboard.html

{% extends "base.html" %}

<div>
{% block body_content %}
Body 3
{% endblock body_content %}
</div>

<div>
{% block app_content %}

DASHBOARD

{% endblock app_content %}
</div>

In this setup, Jinja renders everything except the "DASHBOARD" in the final page. However, when I add an empty block in "base.html", like so..

{% block app_content %}

App content Goes here...

{% endblock app_content %}

In the final template, "DASHBOARD" is printed. Is this some quirk in Jinja? Is this behavior defined in any docs??

Upvotes: 3

Views: 1769

Answers (1)

YellowShark
YellowShark

Reputation: 2269

I understand your issue: you've got template A, template B (extends template A), and template C (extends template B). There's a block defined in template A, but it's not showing up in the page that uses template C, unless you define that block in template B. If you do that, then the block in question shows up.

First: you are correct in your understanding that this is not how the Jinja template hierarchy is supposed to work. Second, I did not encounter your issue (found a different one, though), and I have constructed a proof to demonstrate this. Here's what I did (again, using Python 3 and Django 1.11):

$ python startproject myapp

In the myapp/myapp/settings.py file, I updated the template engine:

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.jinja2.Jinja2',
        'DIRS': [
            os.path.join(BASE_DIR, 'myapp/templates') # hack / should add `myapp` to INSTALLED_APPS instead
        ],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

In myapp/myapp/urls.py, I created a dummy view function & route:

from django.conf.urls import url
from django.shortcuts import render_to_response

def home(request):
    return render_to_response('dashboard.html')

urlpatterns = [
    url(r'^$', home),
]

And finally, I established those templates, but I removed the call to import your macros, along with all instances of static().

Here is the myapp/myapp/templates/skeleton.html file:

<!doctype html>
<html lang="en">
<head>
  {% block head_content %}
    <meta charset="utf-8">
    <title> {% if page_title %} {{ page_title }} | {% endif %} Bhargavi Books & Stationery </title>
  {% endblock head_content %}
</head>

<body>
<div id="navigation">
  {% block navigation %}
    <div class="ui three item menu">
      <a class="active item">Home</a>
      <a class="item">New Bill</a>
      <a class="item">View Bills</a>
    </div>
  {% endblock navigation %}
</div>

<div id="frame">
  {% block frame_content %}
    <p> Body content goes here. Body for {{ content_name }}</p>
  {% endblock frame_content %}
</div>

<div id="app">
  {% block app_content %}
    <p> APP content goes here. Body for {{ content_name }}</p>
  {% endblock app_content %}
</div>

{% block scripts %}
{% endblock scripts %}
</body>
</html>

Here is the myapp/myapp/base.html file:

{% extends "skeleton.html" %}

{% block frame_content %}
  Frame Content
{% endblock frame_content %}

{% block scripts %}
  {{ super() }}
{% endblock scripts %}

And here is the myapp/myapp/templates/dashboard.html file:

{% extends "base.html" %}

<div>
  {% block body_content %}
    Body 3
  {% endblock body_content %}
</div>

<div>
  {% block app_content %}
    DASHBOARD
  {% endblock app_content %}
</div>

And here is the output, after viewing that page in my browser:

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title> Bhargavi Books & Stationery </title>
</head>

<body>
<div id="navigation">
  <div class="ui three item menu">
    <a class="active item">Home</a>
    <a class="item">New Bill</a>
    <a class="item">View Bills</a>
  </div>
</div>

<div id="frame">
  Frame Content
</div>

<div id="app">
  DASHBOARD
</div>
</body>
</html>

Since this all works as expected*, my conclusion is that you might have a problem with the base.html template file. Perhaps it's failing to import your macros, and subsequently not behaving correctly? I notice a trailing comma in there, and I'm not sure if that could be problematic or not. I would suggest using the above code as a starting point, and then slowly add back in the parts that I stripped out, and perhaps the problem will become visible/understandable.

*One thing I found odd, that I don't understand: the body_content block is completely missing from my output, unless I define that block in the skeleton.html template. It doesn't work if I define it in the base.html template, which seems wrong to me, because then we're not really extending that second template (base.html)... so there does seem to be something weird there... but I was not able to encounter the original issue you described, so maybe this will be helpful in that particular regards, at least.

Upvotes: 2

Related Questions