Skip to content

Templates

Theory

The final product of our work, visible to the user, is the website. Thanks to Django, we can generate dynamic pages. Template languages are used for convenient page generation. For isolation of template HTML code, templates are placed in separate files. Templates are filled with values processed by the view and placed in the so-called context template.

Practice

Let's put the following template code in the file viewer/templates/hello.html:

<!DOCTYPE html>
<html>
  <body>
    <h1>Welcome to HollyMovies!</h1>
    {% for adjective in adjectives %}
      {% if adjective != 'cruel' %}
        <p>This is a {{ adjective }} world...</p>
      {% endif %}
    {% endfor %}
  </body>
</html>

To render it use the render function in the view and indicate with what data we want to fill it:

from django.shortcuts import render


def hello(request, s0):
  s1 = request.GET.get('s1', '')
  return render(
    request, template_name='hello.html',
    context={'adjectives': [s0, s1, 'beautiful', 'wonderful']}
  )

Extension

Templates can inherit from each other. In this way, we can share identical code used in multiple places. We do this using the extends tag. First, however, we need to define the base.html base template, in which we will embed the base of the HTML document structure, Bootstrap styles and scripts, and a navigation bar and main content container that is consistent for all pages:

{% load static %}

<html {% if LANGUAGE_CODE %}lang="{{ LANGUAGE_CODE }}"{% endif %}>

  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>HollyMovies</title>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/css/bootstrap.min.css" integrity="sha384-GJzZqFGwb1QTTN6wy59ffF1BuGJpLSa9DkKMp0DgiMDm4iYMj70gZWKYbI706tWS" crossorigin="anonymous">
    <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.6/umd/popper.min.js" integrity="sha384-wHAiFfRlMFy6i5SRaxvfOCifBUQy1xHdJ/yoi7FRNXMRBu5WHdZYu1hA6ZOblgut" crossorigin="anonymous"></script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/js/bootstrap.min.js" integrity="sha384-B0UglyR+jN6CkvvICOB2joaf5I4l3gm9GU6Hc1og6Ls7i6U/mkkaduKaBhlAXv9k" crossorigin="anonymous"></script>
  </head>

  <body>
    <nav class="navbar navbar-expand-lg navbar-dark bg-dark">
      <div class="container fixed">
        <a class="navbar-brand" href="/">HollyMovies</a>
        <button
          class="navbar-toggler" type="button"
          data-toggle="collapse" data-target="#navbarNavAltMarkup"
        >
          <span class="navbar-toggler-icon"></span>
        </button>
        <div class="collapse navbar-collapse" id="navbarNavAltMarkup">
          <div class="navbar-nav">
            <a class="nav-item nav-link active" href="/">
              Home
            </a>
          </div>
        </div>
      </div>
    </nav>
    <div class="container">
      <div class="jumbotron">
        {% block content %}{% endblock %}
      </div>
    </div>
  </body>

</html> 

The base template is extended by the template hello.html:

{% extends "base.html" %}

{% block content %}
  <h1>Welcome to HollyMovies!</h1>
  {% for adjective in adjectives %}
    {% if adjective != 'cruel' %}
      <p>This is a {{ adjective }} world...</p>
    {% endif %}
  {% endfor %}
{% endblock %}

This is how we embed the code of the hello.html template, specifically its content block, inside the content** block in the base.html template.

List of movies

In the new base template, let's base the list of movies added to the base:

{% extends "base.html" %}

{% block content %}
  <h1>Welcome to HollyMovies!</h1>
  <ul>
    {% for movie in movies %}
      <li>
        {{ movie.title }} ({{ movie.released.year }}) - {{ movie.genre.name }}
      </li>
    {% endfor %}
  </ul>
{% endblock %}

Add to the context the variable movies, which is the collection of movies from the database.

from django.shortcuts import render

from viewer.models import Movie


def movies(request):
  return render(
    request, template_name='movies.html',
    context={'movies': Movie.objects.all()}
  )

Let's not forget to rename the view function in urls.py!

from django.contrib import admin
from django.urls import path

from viewer import views
from viewer.models import Genre, Movie

admin.site.register(Genre)
admin.site.register(Movie)

urlpatterns = [
  path('admin/', admin.site.urls),
  path('', views.movies)
]

Applications in Django should not use names that may change due to business requirements that are irrelevant to the logic. Therefore, we should never link to pages from our application directly, but by using key-names that are not visible in the user interface, given with the parameter name of the path function in the urls.py file.

urlpatterns = [
  path('admin/', admin.site.urls),
  path('', views.movies, name='index')
]
<a class="navbar-brand" href="{% url 'index' %}">HollyMovies</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNavAltMarkup">
  <span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNavAltMarkup">
  <div class="navbar-nav">
    <a class="nav-item nav-link active" href="{% url 'index' %}">
      Home
    </a>
  </div>
</div>