Search code examples
pythonflaskjinja2html-rendering

Jinja2 does not render blocks


I am going through a flask tutorial and want to create a blog using flask. For this purpose I took some code from the tutorial and wrote some code myself. The problem is, that the Jinja2 templating engine only seems to render some of the blocks I declared in the templates and I don't know why.

This is what I got so far:

base.html:

<html>
<head>
    {% if title %}
    <title>{{ title }} - microblog</title>
    {% else %}
    <title>Welcome to microblog</title>
    {% endif %}
</head>
<body>
    {% with messages = get_flashed_messages() %}
        {% if messages %}
        <ul>
        {% for message in messages %}
            <li>{{ message }}</li>
        {% endfor %}
        </ul>
        {% endif %}
    {% endwith %}

    <p>---</p>
    {% block header %}{% endblock %}
    <p>---</p>
    {% block navigation %}{% endblock %}
    <!-- other templates can insert themselves as block -->
    <!-- the following block ist called 'content' -->
    {% block content %}{% endblock %}
</body>

now there are the blocks, which extend the base.html:

index.html:

{% extends "base.html" %}
{% block content %}
<h1>Hi, {{ users.nickname }}!</h1>
{% for post in posts %}
<div><p>{{ post.author.nickname }} says: <b>{{ post.body }}</b></p></div>
{% endfor %}
{% endblock %}

header.html:

{% extends "base.html" %}
{% block header %}
<!-- this is the header block -->
<h1>Microblog!</h1>
{% endblock %}

navigation.html (copied from another css dropdown menu tutorial):

{% extends "base.html" %}
{% block navigation %}
<nav id="nav">
<ul id="navigation">
    <li><a href="#" class="first">Home</a></li>
    <li><a href="#">Services &raquo;</a>
        <ul>
            <li><a href="#">Web Development</a></li>
            <li><a href="#">Logo Design</a></li>
            <li><a href="#">Identity & Branding &raquo;</a>
                <ul>
                    <li><a href="#">Business Cards</a></li>
                    <li><a href="#">Brochures</a></li>
                    <li><a href="#">Envelopes</a></li>
                    <li><a href="#">Flyers</a></li>
                </ul>                   
            </li>                   
            <li><a href="#">Wordpress</a></li>
        </ul>
    </li>
    <li><a href="#">Portfolio &raquo;</a>
        <ul>
            <li><a href="#">Graphic Design</a></li>
            <li><a href="#">Photography</a></li>
            <li><a href="#">Architecture</a></li>
            <li><a href="#">Calligraphy</a></li>
            <li><a href="#">Film &raquo;</a>
                <ul>
                    <li><a href="#">John Carter</a></li>
                    <li><a href="#">The Avengers</a></li>
                    <li><a href="#">The Amazing SpiderMan</a></li>
                    <li><a href="#">Madagascar 3</a></li>
                </ul>                       
            </li>
            <li><a href="#">Graffity </a></li>
        </ul>               
    </li>
    <li><a href="#">Testimonials</a></li>
    <li><a href="#">Blog</a></li>
    <li><a href="#" class="last">Contact</a></li>
</ul>
</nav>
{% endblock %}

However, the resulting source code in the browser is:

<html>
<head>

    <title>Home - microblog</title>

</head>
<body>




    <p>---</p>

    <p>---</p>

    <!-- other templates can insert themselves as block -->
    <!-- the following block ist called 'content' -->


<h1>Hi, Miguel!</h1>

<div><p>John says: <b>Beautiful day in Portland!</b></p></div>

<div><p>Susan says: <b>The Avengers movie was so cool!</b></p></div>

<div><p>Xiaolong says: <b>Crouching Tiger Hidden Dragon, one of my favorites …</b></p></div>


</body>
</html>

My views.py is this:

from flask import render_template, flash, redirect
from app import app
from .forms import LoginForm

@app.route('/')
@app.route('/index')
def index():
users = {'nickname': 'Miguel'}  # fake user

posts = [  # fake array of posts
    { 
        'author': {'nickname': 'John'}, 
        'body': 'Beautiful day in Portland!' 
    },
    { 
        'author': {'nickname': 'Susan'}, 
        'body': 'The Avengers movie was so cool!' 
    },
    {
        'author': {'nickname': 'Xiaolong'},
        'body': 'Crouching Tiger Hidden Dragon, one of my favorites …'
    }
]

# Flask uses the jinja templating engine internally, which fills in the variables into the template.
# 1.arg: name of the template file in the templates folder
# 2. - x. arg: values for the variables we want to see in the rendered page
return render_template(
    'index.html',
    title='Home',
    users=users,
    posts=posts
)

What am I doing wrong? Why is only the block content from index.html rendered?

EDIT#1: Clarification: The expected result was Jinja rendering all mentioned blocks in the template and the base template and all others which are mentioned in the ones Jinja sees on it's way through the templates/blocks.

EDIT#2: Putting blocks into index.html: Even if put the blocks into the index.html it does not render them:

{% extends "base.html" %}
{% block content %}
<p>---</p>
{% block header %}{% endblock %}
<p>---</p>
{% block navigation %}{% endblock %}

<h1>Hi, {{ users.nickname }}!</h1>
{% for post in posts %}
<div><p>{{ post.author.nickname }} says: <b>{{ post.body }}</b></p></div>
{% endfor %}
{% endblock %}

Solution

  • You are implementing each block in a different html file, but you render index.html. What Jinja2 does when you tell it to render index.html is grab the base template (base.html) and look at what modification index.html brings - in your case, updating the content block.

    Jinja2 won't even look at the other block implementations (in other words the other html files) in this case. What you want is to implement title/navigation/etc. in base.html itself. The templating engine only looks at the inheritance chain of the template you are currently rendering, it doesn't load all existing templates for each render operation.

    EDIT: Discussed in comments but updating here in case some else runs into this: if you want to render different pieces from different files, use the {%include <template> %} directive, not blocks.