9. Plantillas

“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

Para ver los contenidos que son generados por las vistas que acabamos de crear, ahora necesitas preparar las plantillas.

9.1. El sistema de plantillas de Django

Django tiene su propio sistema de plantillas para crear HTML que no simplemente mezcla código Python y HTML. Esto se hace por una razón: el sistema de plantillas debería utilizarse para mostrar contenidos y no para la lógica de programación.

The Django template system provides various tags to perform simple operations. For example can the if tag be used to execute various tests or the for tag to iterate over a list. However the templates are not executed like regular Python code and the choice of functions is limited to the tags and filters provided by the Django template system. But it is possible to extend the template system with your own tags and filters.

Aparte de eso, el sistema de plantillas de Django tiene una característica de herencia poderosa. Con esta característica es posible usar una plantilla base que contiene todos los elemento básicos del sitio Web y define diferentes bloques. Algunos o todos estos bloques pueden ser sobre escritos o extendidos por plantillas hijas que extienden la plantilla base. Aquellos que están familiarizados con la programación orientada a objetos conocen este patrón.

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. En la sección “H5BL Optional” marca la opción “404 Page”

  3. Finalmente haz click en el botón “Download it!”

  4. Extrae el archivo ZIP

  5. Copia el archivo index.html al directorio mysite/templates'  y cambia el nombre a :file:`base.html

  6. Copia el archivo 404.html al mismo directorio

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

Nota

Para hacer funcionar la descarga del archivo ZIP de HTML5 Boilerplate tu navegador necesita aceptar las cookies del sitio Web.

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>

Ahora sigue los lugares donde la etiqueta static necesita ser colocada al final de la plantilla base.html:

 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>

Ahora reemplaza la etiqueta HTML title con la línea resaltada:

 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">

Aquí, el primer bloque (vacío) sera definido con la ayuda de la etiqueta block.

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

Nota

Si el servidor de desarrollo se ejecutaba mientras se creaban los nuevos directorios, tienes que re iniciarlo. De otra manera no se reconocerán los nuevos directorios.

Ahora crea las plantillas dentro del directorio marcador/templates/marcador.

Primero la plantilla para la vista de lista 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 %}

Esta plantilla extiende la plantilla base base.html con la ayuda de la etiqueta extends. Por ello es posible extender los bloques definidos en la plantilla base. Además, la ya mencionada etiqueta for es usada para iterar sobre la lista de marcadores (bookmarks) y crear una entrada en la lista para cada uno. La plantilla para el marcador es incluída usando la etiqueta include.

Ahora puedes crear la plantilla bookmark.html que ha sido incluida en la anterior plantilla usando la etiqueta include:

 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

En esta plantilla un objeto Bookmark está disponible que es asignado a la variable bookmark. En una plantilla es posible acceder a los atributos de un objeto, por ejemplo el título usando bookmark.title. Los atributos pueden ser modificados por filtro, por ejemplo usando linebreaksbr que convierte todas la etiquetas HTML <br /> en saltos de líneas reales.

También es posible acceder a objetos relacionados, por ejemplo las etiquetas. Ya que bookmark.tags es una lista de objetos representados por objeto manager, sólo puedes acceder a los objetos Tag con la ayuda de métodos definidos por el manager. bookmark.tags.count devuelve el número de etiquetas. bookmark.tags.all crea un iterador que puede ser usado en un bucle. Para invocar a estos métodos en tu código Python tienes que poner un par de paréntesis al final del método (bookmark.tag.count()). Si el método es invocado en una plantilla no se permiten paréntesis porque no se puede pasar argumentos a un método en una plantilla.

Con la ayuda de la etiqueta url un enlace a los otros marcadores del dueño del marcador es creado al final de la plantilla. Ya que el URL marcador_bookmark_user necesita un nombre de usuario, necesitamos pasarlo como argumento a la etiqueta de plantilla. Eso implica que argumentos se pueden pasar a etiquetas de plantillas.

Finalmente, la plantilla para los marcadores del usuario se debe crear, 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 %}

Esta plantilla extiende la plantilla bookmarks_list.html porque sólo hay pequeñas diferencias entre ellas. Sólo tenemos que sobre escribir los bloques title y heading y podemos reutilizar el resto. ¡Muy práctico”

9.4. Test the new templates

Después de que todas las plantillas han sido creadas, puedes iniciar el servidor de desarrollo y echar un vistazo a los marcadores, que han sido creados usando la interfaz de administración, en la interfaz Web bajo http://127.0.0.1:8000/.

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!