9. Templates

“Django’s template language is designed to strike a balance between power and ease. It’s designed to feel comfortable to those used to working with HTML. If you have any exposure to other text-based template languages, such as Smarty or Jinja2, you should feel right at home with Django’s templates.”

The Django template language | Django documentation

Damit die Inhalte, die durch die eben erstellten Views erzeugt werden, auch sichtbar sind müssen nun noch die Templates angelegt werden.

9.1. Das Django Template System

Django hat ein eigenes Templates System zur Erzeugung von HTML, das nicht nur einfach Python Code mit HTML mischt. Das hat einen Grund: das Template System soll zur Darstellung der Inhalte genutzt werden, nicht zum Programmieren von Logik.

Das Django Template System stellt verschiedene Tags zur Verfügung, mit denen einfache Operationen durchgeführt werden können. Zum Beispiel können mit dem if Tag verschiedene Tests durchgeführt werden oder mit dem for Tag über eine Liste iteriert werden. Allerdings werden die Templates nicht wie normaler Python Code ausgeführt und die Auswahl von Funktionen ist auf die Tags und Filter beschränkt, die das Django Template System zur Verfügung stellt. Es ist aber auch möglich das Template System mit eigenen Tags und Filtern zu erweitern.

Außerdem besitzt das Django Template System eine mächtige Vererbungs-Funktion. Damit ist es möglich ein Basis-Template zu nutzen, dass alle wichtigen Elemente der Website enthält und verschiedene Blöcke definiert. Einige oder alle dieser Blöcke können dann von mehreren Kind-Templates, die vom Basis-Template erben, überschrieben oder erweitert werden. Wer mit Objektorientierter Programmierung vertraut ist wird dieses Modell kennen.

9.2. Create the base template

At first create the base template with the help of the HTML5 Boilerplate. It comes together with Bootstrap 3, a popular HTML and CSS framework for developing responsive, mobile first websites and jQuery, a fast, small, and feature-rich JavaScript library. Perform the following steps to download and copy the files:

  1. Click at the Initializr Website on “Bootstrap”
  2. Im Abschnitt “H5BP Optional” wählst du “404 Page” aus

  3. Zum Schluss klickst du auf den Button “Download it!”

  4. Entpacke das ZIP Archiv

  5. Kopiere die Datei index.html in das Verzeichnis mysite/templates und bennene sie in base.html um

  6. Kopiere die Datei 404.html in das gleiche Verzeichnis

  7. Copy the directories css, img and js and the file apple-touch-icon.png into the directory mysite/static

Bemerkung

Damit der Download des HTML5 Boilerplate ZIP Archivs funktioniert muss dein Browser die von der Website gesetzten Cookies akzeptieren.

Now you need to adjust the base template base.html so that Django is able to able to use the right path for the static files. Therefor you will use the static template tag from the staticfiles app. Template tags are written this way: {% tag %}. Because the tag static is provided by an app it’s not part of the default template tags and needs to be loaded first. This is done with the help of the load tag at the top of the template. Customize all highlighted parts in the head of base.html as shown:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
{% load staticfiles %}
<!doctype html>
<!--[if lt IE 7]>      <html class="no-js lt-ie9 lt-ie8 lt-ie7" lang=""> <![endif]-->
<!--[if IE 7]>         <html class="no-js lt-ie9 lt-ie8" lang=""> <![endif]-->
<!--[if IE 8]>         <html class="no-js lt-ie9" lang=""> <![endif]-->
<!--[if gt IE 8]><!--> <html class="no-js" lang=""> <!--<![endif]-->
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
        <title></title>
        <meta name="description" content="">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <link rel="apple-touch-icon" href="{% static "apple-touch-icon.png" %}">

        <link rel="stylesheet" href="{% static "css/bootstrap.min.css" %}">
        <style>
            body {
                padding-top: 50px;
                padding-bottom: 20px;
            }
        </style>
        <link rel="stylesheet" href="{% static "css/bootstrap-theme.min.css" %}">
        <link rel="stylesheet" href="{% static "css/main.css" %}">

        <script src="{% static "js/vendor/modernizr-2.8.3-respond-1.4.2.min.js" %}"></script>
    </head>
    <body>

Hier folgen die Stellen, an denen das static Tag am Ende des Templates base.html einzusetzen ist:

 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
      <footer>
        <p>&copy; Company 2015</p>
      </footer>
    </div> <!-- /container -->        <script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
        <script>window.jQuery || document.write('<script src="{% static "js/vendor/jquery-1.11.2.min.js" %}"><\/script>')</script>

        <script src="{% static "js/vendor/bootstrap.min.js" %}"></script>

        <script src="{% static "js/main.js" %}"></script>

        <!-- Google Analytics: change UA-XXXXX-X to be your site's ID. -->
        <script>
            (function(b,o,i,l,e,r){b.GoogleAnalyticsObject=l;b[l]||(b[l]=
            function(){(b[l].q=b[l].q||[]).push(arguments)});b[l].l=+new Date;
            e=o.createElement(i);r=o.getElementsByTagName(i)[0];
            e.src='//www.google-analytics.com/analytics.js';
            r.parentNode.insertBefore(e,r)}(window,document,'script','ga'));
            ga('create','UA-XXXXX-X','auto');ga('send','pageview');
        </script>
    </body>
</html>

Nun ersetzt du das title HTML Tag mit der markierten Zeile:

 7
 8
 9
10
11
12
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <title>Marcador - {% block title %}{% endblock %}</title>
    <meta name="description" content="">
    <meta name="viewport" content="width=device-width, initial-scale=1">

Hier wird der erste (leere) Block mit Hilfe des block Tags definiert.

Delete the <div> HTML tag overwritten with <!-- Main jumbotron for a primary marketing message or call to action --> including it’s contents. Here can see the lines you have to delete:

56
57
58
59
60
61
62
63
<!-- Main jumbotron for a primary marketing message or call to action -->
<div class="jumbotron">
  <div class="container">
    <h1>Hello, world!</h1>
    <p>This is a template for a simple marketing or informational website. It includes a large callout called a jumbotron and three supporting pieces of content. Use it as a starting point to create something more unique.</p>
    <p><a class="btn btn-primary btn-lg" href="#" role="button">Learn more &raquo;</a></p>
  </div>
</div>

Below you will see another <div> HTML tag follow by <!-- Example row of columns -->. Delete the HTML comment and the following HTML tag <div class="row"> including it’s contents. Here can see the lines you have to delete:

66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
<!-- Example row of columns -->
<div class="row">
  <div class="col-md-4">
    <h2>Heading</h2>
    <p>Donec id elit non mi porta gravida at eget metus. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Etiam porta sem malesuada magna mollis euismod. Donec sed odio dui. </p>
    <p><a class="btn btn-default" href="#" role="button">View details &raquo;</a></p>
  </div>
  <div class="col-md-4">
    <h2>Heading</h2>
    <p>Donec id elit non mi porta gravida at eget metus. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Etiam porta sem malesuada magna mollis euismod. Donec sed odio dui. </p>
    <p><a class="btn btn-default" href="#" role="button">View details &raquo;</a></p>
 </div>
  <div class="col-md-4">
    <h2>Heading</h2>
    <p>Donec sed odio dui. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Vestibulum id ligula porta felis euismod semper. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus.</p>
    <p><a class="btn btn-default" href="#" role="button">View details &raquo;</a></p>
  </div>
</div>

Now add two additional blocks to the remaining <div> HTML tag; one for the headline and the other for the contents of the current page:

50
51
52
53
54
55
56
57
58
59
60
61
62
63
<div class="container">
  <div>
      {% block heading %}{% endblock %}
  </div>
  <div>
      {% block content %}{% endblock %}
  </div>

  <hr>

  <footer>
    <p>&copy; Company 2015</p>
  </footer>
</div> <!-- /container -->        <script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>

That’s it! The base template is done!

9.3. Create the templates for the views

Now the base template is finished. The next step is to create the missing templates for the views. First create the appropriate directory structure:

$ mkdir -p marcador/templates/marcador

This structure is necessary so that the template loader can load the templates. After that the app marcador has this structure:

marcador
|-- __init__.py
|-- admin.py
|-- migrations
|   |-- 0001_initial.py
|   `-- __init__.py
|-- models.py
|-- templates
|   `-- marcador
|-- tests.py
|-- urls.py
`-- views.py

Bemerkung

Falls der Entwicklungsserver während des Anlegens der neuen Verzeichnisse lief, muss er neu gestartet werden, da die neuen Verzeichnisse sonst nicht bekannt sind.

Im Verzeichnis marcador/templates/marcador legst du nun die Templates an.

Zuerst das Template für die Listenansicht bookmark_list.html:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
{% extends "base.html" %}

{% block title %}Latest bookmarks{% endblock %}

{% block heading %}
  <h2>Latest bookmarks</h2>
{% endblock %}

{% block content %}
  <ul class="list-unstyled">
  {% for bookmark in bookmarks %}
    <li class="well well-sm">{% include "marcador/bookmark.html" %}</li>
  {% empty %}
    <li>No bookmarks. :(</li>
  {% endfor %}
  </ul>
{% endblock %}

Dieses Template erbt mit Hilfe des Tags extends vom Basis-Template base.html. Deshalb ist es möglich die im Basis-Template definierten Blöcke hier zu überschreiben. Außerdem wird das schon erwähnte Tag for benutzt, um über die Liste der Lesezeichen (bookmarks) zu iterieren und für jedes einen Listeneintrag zu erzeugen. Das Template für das Lesezeichen wird mit einem include Tag eingebunden.

Nun legst du das Template an, das im vorhergehenden mit Hilfe von include eingebunden wurde, bookmark.html:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
<a class="lead" href="{{ bookmark.url }}">{{ bookmark.title }}</a>
{% if bookmark.description %}
  <br>{{ bookmark.description|linebreaksbr }}
{% endif %}
{% if not bookmark.is_public %}
  <br><span class="label label-warning">private</span>
{% else %}
    <br>
{% endif %}
{% if bookmark.tags.count %}
  {% for tag in bookmark.tags.all %}
    <span class="label label-primary">{{ tag|lower }}</span>&nbsp;
  {% endfor %}
{% endif %}
<br>by <a href="{% url "marcador_bookmark_user" bookmark.owner.username %}">
    {{ bookmark.owner.username }}</a>
{{ bookmark.date_created|timesince }} ago

In diesem Template steht ein Bookmark-Objekt zur Verfügung, das an den Bezeichner bookmark gebunden ist. Im Template ist es möglich auf die Attribute des Objekts zuzugreifen, zum Beispiel mit bookmark.title auf den Titel. Die Attribute können durch Filter verändert werden, wie zum Beispiel durch linebreaksbr, dass alle Zeilenumbrüche zu <br /> HTML Tags umwandelt.

Es ist auch möglich auf die verbundenen Objekte zuzugreifen, wie zum Beispiel die Tags. Da bookmark.tags aber eine Liste von Objekten ist, die durch ein Manager-Objekt repräsentiert wird, können wir nur mit Hilfe von Methoden auf die Tag-Objekte zugreifen. bookmark.tags.count liefert die Anzahl der Tags. bookmark.tags.all erzeugt einen Iterator, der mit einem Loop benutzt werden kann. Würde man diese Methoden in Python aufrufen, muss ein Klammerpaar am Ende der Methode stehen (bookmark.tags.count()). Werden die Methoden aber im Template aufgerufen, müssen die Klammern weggelassen werden, denn an Methoden kann man im Template keine Argumente übergeben.

Am Ende des Templates wird mit Hilfe des url Tags ein Link zur Liste der Lesezeichen des Benutzers erzeugt, dem dieses Lesezeichen gehört. Da der URL marcador_bookmark_user einen Benutzernamen benötigt, müssen wir diesen als Argument an das Template Tag übergeben. An Template Tags können also Argumente übergeben werden.

Zuletzt muss dann das Template für die Lesezeichen eines Benutzers erstellt werden, bookmark_user.html:

1
2
3
4
5
6
7
8
9
{% extends "marcador/bookmark_list.html" %}

{% block title %}{{ owner.username }}'s bookmarks{% endblock %}

{% block heading %}
  <h2>{{ owner.username }}'s bookmarks<br>
    <small>{{ bookmarks.count }} bookmarks in total</small>
  </h2>
{% endblock %}

Dieses Template erbt vom Template bookmark_list.html, da es sich nur wenig von diesem unterscheidet. Wir brauchen lediglich die Blöcke title und heading überschreiben, alles andere können wir wieder benutzen. Praktisch!

9.4. Test the new templates

Nachdem alle Templates angelegt wurden startest du den Entwicklungsserver und kannst dir die Lesezeichen, die du im Admin angelegt hast, im Frontend unter http://127.0.0.1:8000/ ansehen.

The list of bookmarks should look like this:

Frontend Bookmark List View

The login form at the top of the page does not work yet. But you will finish it right in the next chapter!