How to write this Django HTML template more efficiently?

Viewed 50

I have a Django app where users can create their lists and add to-dos. I want to add a new page which will show to-dos that had due dates. Like, in the page, there will be a section called "Earlier" which was feature tasks with missed due dates. Other sections would be "Today", "Tomorrow" and "Later on". Let me show my view class and HTML code now and explain my problem.

Class based view to handle this page:

class ToDoNextUpView(LoginRequiredMixin, ListView):
    model = ToDo
    template_name = "ToDo/next_up.html"
    ordering = ["-due_date"]
    context_object_name = "todos"

    def get_queryset(self):
        query_set = []
        for todo in ToDo.objects.filter(creator=self.request.user, is_checked=False):
            if todo.due_date is not None:
                query_set.append(todo)

        return query_set

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        today = datetime.datetime.now(datetime.timezone.utc)
        tomorrow = today + datetime.timedelta(days=1)

        todos_earlier = []
        todos_today = []
        todos_tomorrow = []
        todos_later = []

        for todo in ToDo.objects.filter(creator=self.request.user, is_checked=False):
            if todo.due_date is not None:
                if todo.due_date.day == today.day and todo.due_date.month == today.month and todo.due_date.year == today.year:
                    todos_today.append(todo)
                elif todo.due_date.day == tomorrow.day and todo.due_date.month == tomorrow.month and todo.due_date.year == tomorrow.year:
                    todos_tomorrow.append(todo)
                elif todo.due_date < today:
                    todos_earlier.append(todo)
                elif todo.due_date > tomorrow:
                    todos_later.append(todo)

        context["todos_earlier"] = todos_earlier
        context["todos_today"] = todos_today
        context["todos_tomorrow"] = todos_tomorrow
        context["todos_later"] = todos_later


        return context

And this is next_up.html

{% extends "ToDo/base.html" %}
{% block content %}
{% load crispy_forms_tags %}

{% if todos_earlier %}
    <center><h1 style="font-family: Cambria, Cochin, Georgia, Times, 'Times New Roman', serif; font-size: 10ch; background-color: royalblue;">Earlier</h1></center>
    <br>
    {% for todo in todos_earlier %}
        <article class="content-section">
            <div class="media-body full-content">
                <h2>
                    <button onclick="location.href='{% url 'todo-check' todo.pk %}'" class="btn btn-outline-success">
                        <i class="fa fa-check"></i>
                    </button> 
                    <a style="overflow: hidden; text-overflow: ellipsis;" href="{% url 'todo-detailed' todo.title todo.pk %}" class="article-title">  {{ todo.title }}</a> 
                    <button data-toggle="modal" data-target="#exampleModalCenter{{ todo.pk }}" style="float: right;" class="btn btn-outline-danger">
                        <i class="fas fa-trash"></i>
                    </button>
                    {% if not todo.important %}
                        <button style="float: right; margin-right: 5px;" onclick="location.href='{% url 'todo-toggle-important' todo.pk %}'"
                            type="submit" class="btn btn-secondary">
                            <span aria-hidden="true"><i class="fas fa-star"></i></span>
                        </button>
                        {% else %}
                        <button style="float: right; margin-right: 5px;" onclick="location.href='{% url 'todo-toggle-important' todo.pk %}'"
                            type="submit" class="btn btn-warning">
                            <span aria-hidden="true"><i class="fas fa-star"></i></span>
                        </button>
                    {% endif %}
                </h2>
                <h3 style="color:red">
                    Was due on: {{ todo.due_date|date:"F d" }}
                </h3>
            </div>
        </article>
    {% endfor %}
{% endif %}

{% if todos_today %}
    <center><h1 style="font-family: Cambria, Cochin, Georgia, Times, 'Times New Roman', serif; font-size: 10ch; background-color: royalblue;">For Today</h1></center>
    <br>
    {% for todo in todos_today %}
        <article class="content-section">
            <div class="media-body full-content">
                <h2>
                    <button onclick="location.href='{% url 'todo-check' todo.pk %}'" class="btn btn-outline-success">
                        <i class="fa fa-check"></i>
                    </button> 
                    <a style="overflow: hidden; text-overflow: ellipsis;" href="{% url 'todo-detailed' todo.title todo.pk %}" class="article-title">  {{ todo.title }}</a> 
                    <button data-toggle="modal" data-target="#exampleModalCenter{{ todo.pk }}" style="float: right;" class="btn btn-outline-danger">
                        <i class="fas fa-trash"></i>
                    </button>
                    {% if not todo.important %}
                        <button style="float: right; margin-right: 5px;" onclick="location.href='{% url 'todo-toggle-important' todo.pk %}'"
                            type="submit" class="btn btn-secondary">
                            <span aria-hidden="true"><i class="fas fa-star"></i></span>
                        </button>
                        {% else %}
                        <button style="float: right; margin-right: 5px;" onclick="location.href='{% url 'todo-toggle-important' todo.pk %}'"
                            type="submit" class="btn btn-warning">
                            <span aria-hidden="true"><i class="fas fa-star"></i></span>
                        </button>
                    {% endif %}
                </h2>
                <h3 style="color:blue">
                    Due today
                </h3>
            </div>
        </article>
    {% endfor %}
{% endif %}

{% if todos_tomorrow %}
    <center><h1 style="font-family: Cambria, Cochin, Georgia, Times, 'Times New Roman', serif; font-size: 10ch; background-color: royalblue;">For Tomorrow</h1></center>
    <br>
    {% for todo in todos_tomorrow %}
        <article class="content-section">
            <div class="media-body full-content">
                <h2>
                    <button onclick="location.href='{% url 'todo-check' todo.pk %}'" class="btn btn-outline-success">
                        <i class="fa fa-check"></i>
                    </button> 
                    <a style="overflow: hidden; text-overflow: ellipsis;" href="{% url 'todo-detailed' todo.title todo.pk %}" class="article-title">  {{ todo.title }}</a> 
                    <button data-toggle="modal" data-target="#exampleModalCenter{{ todo.pk }}" style="float: right;" class="btn btn-outline-danger">
                        <i class="fas fa-trash"></i>
                    </button>
                    {% if not todo.important %}
                        <button style="float: right; margin-right: 5px;" onclick="location.href='{% url 'todo-toggle-important' todo.pk %}'"
                            type="submit" class="btn btn-secondary">
                            <span aria-hidden="true"><i class="fas fa-star"></i></span>
                        </button>
                        {% else %}
                        <button style="float: right; margin-right: 5px;" onclick="location.href='{% url 'todo-toggle-important' todo.pk %}'"
                            type="submit" class="btn btn-warning">
                            <span aria-hidden="true"><i class="fas fa-star"></i></span>
                        </button>
                    {% endif %}
                </h2>
                <h3 style="color:green">
                    Due tomorrow
                </h3>
            </div>
        </article>
    {% endfor %}
{% endif %}

{% if todos_later %}
    <br>
    <center><h1 style="font-family: Cambria, Cochin, Georgia, Times, 'Times New Roman', serif; font-size: 10ch; background-color: royalblue;">Coming up later</h1></center>
    <br>
    {% for todo in todos_later %}
        <article class="content-section">
            <div class="media-body full-content">
                <h2>
                    <button onclick="location.href='{% url 'todo-check' todo.pk %}'" class="btn btn-outline-success">
                        <i class="fa fa-check"></i>
                    </button> 
                    <a style="overflow: hidden; text-overflow: ellipsis;" href="{% url 'todo-detailed' todo.title todo.pk %}" class="article-title">  {{ todo.title }}</a> 
                    <button data-toggle="modal" data-target="#exampleModalCenter{{ todo.pk }}" style="float: right;" class="btn btn-outline-danger">
                        <i class="fas fa-trash"></i>
                    </button>
                    {% if not todo.important %}
                        <button style="float: right; margin-right: 5px;" onclick="location.href='{% url 'todo-toggle-important' todo.pk %}'"
                            type="submit" class="btn btn-secondary">
                            <span aria-hidden="true"><i class="fas fa-star"></i></span>
                        </button>
                        {% else %}
                        <button style="float: right; margin-right: 5px;" onclick="location.href='{% url 'todo-toggle-important' todo.pk %}'"
                            type="submit" class="btn btn-warning">
                            <span aria-hidden="true"><i class="fas fa-star"></i></span>
                        </button>
                    {% endif %}
                </h2>
                <h3 style="color:seagreen">
                    Due on: {{ todo.due_date|date:"F d" }}
                </h3>
            </div>
        </article>
    {% endfor %}
{% endif %}

{% endblock content %}

As you can see, I have written almost the exact same HTML code for every one of those sections. Although this technically works but I would never be able to live with myself. I have to write those check buttons four times, star buttons four times and everything else four times. This cannot be good.

Also, in my View, I have made some sloppy coding where I checked the date, month and year which didn't seem right. But I am aware that checking only the day is not good since same days can of be different months. And to be clear, these are DateTime objects so I cannot just equal them since the precise seconds won't match. But I guess this could still be okay. My main question here is what changes should I bring about in my HTML and Python code so that I can display all the sections within a single for loop (or maybe nested loops but I don't want to four same code four times).

Thanks.

1 Answers

I believe you should use Django's built-in template tag include for each todo section and for those includes make a file for each one of them extending a base generic one with the shared logic.

Below i show a example that could be used, now instead of writing 4 times the code just edit todo_generic.html

next_up.html

{% extends "ToDo/base.html" %}
{% block content %}
{% load crispy_forms_tags %}

{% if todos_earlier %}
    <center><h1 style="font-family: Cambria, Cochin, Georgia, Times, 'Times New Roman', serif; font-size: 10ch; background-color: royalblue;">Earlier</h1></center>
    <br>
    {% include "todos_earlier.html" with todos=todos_earlier %}
{% endif %}

{% if todos_today %}
    <center><h1 style="font-family: Cambria, Cochin, Georgia, Times, 'Times New Roman', serif; font-size: 10ch; background-color: royalblue;">For Today</h1></center>
    <br>
    {% include "todos_today.html" with todos=todos_today %}
{% endif %}

{% if todos_tomorrow %}
    <center><h1 style="font-family: Cambria, Cochin, Georgia, Times, 'Times New Roman', serif; font-size: 10ch; background-color: royalblue;">For Tomorrow</h1></center>
    <br>
    {% include "todos_tomorrow.html" with todos=todos_tomorrow %}
{% endif %}

{% if todos_later %}
    <br>
    <center><h1 style="font-family: Cambria, Cochin, Georgia, Times, 'Times New Roman', serif; font-size: 10ch; background-color: royalblue;">Coming up later</h1></center>
    <br>
    {% include "todos_later.html" with todos=todos_later %}
{% endif %}

{% endblock content %}

todo_generic.html

{% for todo in todos %}
    <article class="content-section">
        <div class="media-body full-content">
            <h2>
                <button onclick="location.href='{% url 'todo-check' todo.pk %}'" class="btn btn-outline-success">
                    <i class="fa fa-check"></i>
                </button> 
                <a style="overflow: hidden; text-overflow: ellipsis;" href="{% url 'todo-detailed' todo.title todo.pk %}" class="article-title">  {{ todo.title }}</a> 
                <button data-toggle="modal" data-target="#exampleModalCenter{{ todo.pk }}" style="float: right;" class="btn btn-outline-danger">
                    <i class="fas fa-trash"></i>
                </button>
                {% if not todo.important %}
                    <button style="float: right; margin-right: 5px;" onclick="location.href='{% url 'todo-toggle-important' todo.pk %}'"
                        type="submit" class="btn btn-secondary">
                        <span aria-hidden="true"><i class="fas fa-star"></i></span>
                    </button>
                {% else %}
                    <button style="float: right; margin-right: 5px;" onclick="location.href='{% url 'todo-toggle-important' todo.pk %}'"
                        type="submit" class="btn btn-warning">
                        <span aria-hidden="true"><i class="fas fa-star"></i></span>
                    </button>
                {% endif %}
            </h2>
            {% block due %}{% endblock %}
        </div>
    </article>
{% endfor %}

todos_earlier.html

{% extends "todo_generic.html" %}
{% block due %}
<h3 style="color:red">
    Was due on: {{ todo.due_date|date:"F d" }}
</h3>
{% endblock due %}

todos_today.html

{% extends "todo_generic.html" %}
{% block due %}
<h3 style="color:blue">
    Due today
</h3>
{% endblock due %}

todos_tomorrow.html

{% extends "todo_generic.html" %}
{% block due %}
<h3 style="color:green">
    Due tomorrow
</h3>
{% endblock due %}

todos_later.html

{% extends "todo_generic.html" %}
{% block due %}
<h3 style="color:seagreen">
    Due on: {{ todo.due_date|date:"F d" }}
</h3>
{% endblock due %}
Related