Recently at work, I had a quite long code review from a very young, very rushed co-worker. Let many such reviews (my own included) there were several instances of copy-paste coding, where the same block of code is copied to several locations with only slight modifications.
This was fresh in my mind when I started in on this series of tutorials (which are quite good): Building Data Products with Python
In the middle of the first page, there is a great description of how I have been doing copy-paste coding in the KidTasks app – navigation links in the templates.
Fortunately for me, they also give a good example of how to fix this by using the extends tag to reference shared template code, similar to using a base class.
The Problem
Two of my templates end with the following four lines of code
<br/> <a href="{% url 'task_new' %}?from={{ request.path|urlencode }}">Add New Task</a> <br/> <a href="{% url 'rep_task_new' %}?from={{ request.path|urlencode }}">Add New Repeating Task</a> <br/> <a href="{% url 'kid_new' %}?from={{ request.path|urlencode }}">Add New Kid</a> <br/> <a href="{% url 'today' %}">View Today's Tasks</a>
While there are currently only two instances of this, there are likely to be more in the future (or in a real app).
The Fix
Fortunately, the solution was quite simple. First you create the base template which includes the shared code and some blocks for parts of the template content. In my case, for such simple pages, there was really only a title block and a content block. The base template looks like this:
<div> <h1>{% block title %}(no title){% endblock %}</h1> {% block content %}(no content){% endblock %} <nav> <div id="navbar"> <br/> <a href="{% url 'task_new' %}?from={{ request.path|urlencode }}">Add New Task</a> <br/> <a href="{% url 'rep_task_new' %}?from={{ request.path|urlencode }}">Add New Repeating Task</a> <br/> <a href="{% url 'kid_new' %}?from={{ request.path|urlencode }}">Add New Kid</a> <br/> <a href="{% url 'today' %}">View Today's Tasks</a> </div> </nav> </div>
Note that this puts the navigation links on the bottom of the page (where they were previously). It also adds a div with an id to the nav bar which, I suspect, is going to be handy when I start diving in to CSS and making the site look a bit more professional.
Another thing to note is that this will also move the formatting of the title for these pages to the base template rather than having it in each individual template. This is helpful for maintaining a consistent look throughout your app.
The change to the sub-templates was quite small. First I had to move the title from inside the content block into its own block:
{% block title %} Today's Tasks {% endblock %}
…and then all I had to do was use the extends tag to reference the base template:
{% extends 'tasks/base.html' %}
NOTE: the extends tag is required to be the first line in the template. Django will complain loudly if it is not.
One of the full sub-templates ended up looking like this:
{% extends 'tasks/base.html' %} {% block title %} Today's Tasks {% endblock %} {% block content %} {% if kids %} {% for name, tasks in kids.items %} <h1>{{ name }} on {{ day }}</h1> {% if tasks %} <ul> {% for task in tasks %} <li><a href="{% url 'update' task.id %}"> {% if task.completed %} <strike> {% endif %} {{ task.name }} {% if task.completed %} </strike> {% endif %} </a></li> {% endfor %} </ul> {% else %} <p>No tasks for {{ name }}.</p> {% endif %} {% endfor %} {% else %} <p>No kids are present.</p> {% endif %} {% endblock %}
That’s all there is to it! Thanks to Jose A Dianes for the well-written tutorial referenced above.