7. HTML¶
This chapter will guide you through converting your list of names into a grid with photographs.
7.1. Meet Bootstrap¶
Bootstrap is a free toolkit for designing the cosmetic face of web applications. It is a collection of ready-to-use pieces you can mix and match to jumpstart a project. It includes useful things that you might include in any project, like buttons, modals and dropdowns.
We aim to create a grid of victims where each block will have a picture and some basic information. To do this, we’re going to use the “cards” component included in Bootstrap’s version 5. Cards are self-contained boxes of information which can be arranged and grouped on a page any way you want.
7.3. Add images¶
Now let’s add each victim’s image to their card.
{% for obj in harvard_park_homicides %}
<div class="card">
<img class="card-img-top" src="{{ obj.image }}" alt="{{ obj.first_name }} {{ obj.last_name }}">
<div class="card-body">
<h5 class="card-title">{{ obj.first_name }} {{ obj.last_name }}</h5>
<p class="card-text">{{ obj.last_name }}, a {{ obj.age}}-year-old {{ obj.race }} {{ obj.gender }}, died in {{ obj.death_year }}.</p>
</div>
</div>
{% endfor %}
Ugh. You save and nothing changes on localhost. What’s wrong? When in doubt, take a look at your terminal. You should see something like this:
Baker has raised an error. Look carefully and you can see it’s telling you that attempted to output null or undefined value
. This means you tried to print something in your template that doesn’t exist. Check your data and you’ll see that not every victim has an image. So trying to print one for every iteration of the loop caused a crash.
To fix it, let’s add an {% if %}
clause around the image tag to check for an image in the data. This way, our code will loop through the list of victims and if there is an image it will add it to the right card. If not, the code will skip ahead to the next row.
{% for obj in harvard_park_homicides %}
<div class="card">
{% if obj.image %}<img class="card-img-top" src="{{ obj.image }}" alt="{{ obj.first_name }} {{ obj.last_name }}">{% endif %}
<div class="card-body">
<h5 class="card-title">{{ obj.first_name }} {{ obj.last_name }}</h5>
<p class="card-text">{{ obj.last_name }}, a {{ obj.age}}-year-old {{ obj.race }} {{ obj.gender }}, died in {{ obj.death_year }}.</p>
</div>
</div>
{% endfor %}
The error should go away and you should see something more like this in your browser.
Phew. That’s better, but still far from what we want. What we’ve got so far is a grid that doesn’t look much like a grid. In fact it’s not a grid at all. It’s just a big stack.
7.4. Create a grid¶
To arrange our cards using Bootstrap’s system, we need to play by Bootstrap’s rules.
Look at its documentation and you’ll see that Bootstrap asks us to wrap our cards in a div with the class row
and a special attribute required by Masonry, the Javascript tool that will layout the page. It also wants us to use some special Bootstrap classes to dictate how wide our cards will be.
Make the following changes. Remember there’s no shame in using copy and paste. And don’t miss that the new division is outside of our for loop, meaning it only appears on the page once with all of the cards inside of it.
<div class="row" data-masonry='{"percentPosition": true }'>
{% for obj in harvard_park_homicides %}
<div class="col-sm-6 col-lg-4 mb-4">
<div class="card">
{% if obj.image %}<img class="card-img-top" src="{{ obj.image }}" alt="{{ obj.first_name }} {{ obj.last_name }}">{% endif %}
<div class="card-body">
<h5 class="card-title">{{ obj.first_name }} {{ obj.last_name }}</h5>
<p class="card-text">{{ obj.last_name }}, a {{ obj.age}}-year-old {{ obj.race }} {{ obj.gender }}, died in {{ obj.death_year }}.</p>
</div>
</div>
</div>
{% endfor %}
</div>
We want to be able to click on each card and be redirected to the victim’s page on the Los Angeles Times Homicide Report site. While we’re at it, let’s add some <strong>
tags to the victims’ names to make them stand out from the sentence about them.
<div class="row" data-masonry='{"percentPosition": true }'>
{% for obj in harvard_park_homicides %}
<div class="col-sm-6 col-lg-4 mb-4">
<div class="card">
{% if obj.image %}<img class="card-img-top" src="{{ obj.image }}" alt="{{ obj.first_name }} {{ obj.last_name }}">{% endif %}
<div class="card-body">
<a href="http://homicide.latimes.com/post/{{ obj.slug }}" target="_blank">
<strong>
<h5 class="card-title">{{ obj.first_name }} {{ obj.last_name }}</h5>
</strong>
</a>
<p class="card-text">{{ obj.last_name }}, a {{ obj.age}}-year-old {{ obj.race }} {{ obj.gender }}, died in {{ obj.death_year }}.</p>
</div>
</div>
</div>
{% endfor %}
</div>
7.5. Add supporting elements¶
Now let’s start to wrap things up by writing a headline for our cards section.
<h3>Lives lost in Harvard Park</h3>
<div class="row" data-masonry='{"percentPosition": true }'>
{% for obj in harvard_park_homicides %}
<div class="col-sm-6 col-lg-4 mb-4">
<div class="card">
{% if obj.image %}<img class="card-img-top" src="{{ obj.image }}" alt="{{ obj.first_name }} {{ obj.last_name }}">{% endif %}
<div class="card-body">
<a href="http://homicide.latimes.com/post/{{ obj.slug }}" target="_blank">
<strong>
<h5 class="card-title">{{ obj.first_name }} {{ obj.last_name }}</h5>
</strong>
</a>
<p class="card-text">{{ obj.last_name }}, a {{ obj.age}}-year-old {{ obj.race }} {{ obj.gender }}, died in {{ obj.death_year }}.</p>
</div>
</div>
</div>
{% endfor %}
</div>
And now, some introductory text. We can use a new templating trick, the length
filter, to insert some automatically generated statistics into the text.
<h3>Lives lost in Harvard Park</h3>
<p>
The {{ harvard_park_homicides|length }} homicides in Harvard Park since 2000
were primarily black and Latino males,but the list includes husbands, wives,
fathers, mothers of all ages, and even some small children.
</p>
<div class="row" data-masonry='{"percentPosition": true }'>
{% for obj in harvard_park_homicides %}
<div class="col-sm-6 col-lg-4 mb-4">
<div class="card">
{% if obj.image %}<img class="card-img-top" src="{{ obj.image }}" alt="{{ obj.first_name }} {{ obj.last_name }}">{% endif %}
<div class="card-body">
<a href="http://homicide.latimes.com/post/{{ obj.slug }}" target="_blank">
<strong>
<h5 class="card-title">{{ obj.first_name }} {{ obj.last_name }}</h5>
</strong>
</a>
<p class="card-text">{{ obj.last_name }}, a {{ obj.age}}-year-old {{ obj.race }} {{ obj.gender }}, died in {{ obj.death_year }}.</p>
</div>
</div>
</div>
{% endfor %}
</div>
7.6. Commit your work¶
We’re all done here. So let’s commit our work for this chapter.
First add.
git add .
Then commit.
$ git commit -m "Created a victims card grid"
Then push.
git push origin main