Thumbnail: django

La Biblioteca, aplicación en Django 1.8 | Parte VII

por , en la categoría de Django
9 minuto(s) de lectura
Este artículo hace parte de la serie «Django dummies»:
  1. La Biblioteca, aplicación en Django 1.8 | Parte I
  2. La Biblioteca, aplicación en Django 1.8 | Parte II
  3. La Biblioteca, aplicación en Django 1.8 | Parte III
  4. La Biblioteca, aplicación en Django 1.8 | Parte IV
  5. La Biblioteca, aplicación en Django 1.8 | Parte V
  6. La Biblioteca, aplicación en Django 1.8 | Parte VI
  7. La Biblioteca, aplicación en Django 1.8 | Parte VII

Introducción

Este es el último post de una serie de entradas que abarcan la construcción de una aplicación usando Django en su versión 1.8, hemos estudiado algunos conceptos clave que nos ofrece este genial framework web, la cita que nos reúne hoy es la herencia de templates, otra característica muy potente que vale la pena aprender a usar, tambien vamos a mejorar un poco el look and feel de nuestra aplicación, poniendo algo de estilo aquí y allá. Vamos a ello.

Antes de comenzar

Primero que todo debemos de tener configurado correctamente nuestros archivos estáticos, para ello te recomiendo leer este post donde se explica claramente como hacer este proceso. No es complicado, solo es configurar unas cuantas variables, léelo. Para hacer las cosas mucho mas ágiles vamos a usar Materialize un framework CSS basado en material design.

Herencia de templates en nuestra aplicación

En el post anterior cubrimos la parte teórica de las templates en Django, explicando qué son y para qué nos sirven, solo diré que es una característica muy potente que nos permite ahorrar código y ordenar mejor nuestras interfaces.

Vamos a crear un nuevo archivo al que llamaremos base.html (dentro de la carpeta de templates), en este archivo vamos a definir todo el código html común para todas las otras templates que ya hemos creado en post anteriores. Coloca lo siguiente:


<!DOCTYPE html>
<html lang="es">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
  <!-- jquery minificado -->
  <script src="https://code.jquery.com/jquery-2.1.4.min.js"></script>
  <!--Materialize-->
  <!-- Compiled and minified CSS -->
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.97.0/css/materialize.min.css">
  <!-- Compiled and minified JavaScript -->
  <script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.97.0/js/materialize.min.js"></script>
  <!--Iconos de Materialize-->
  <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
  <title>{% block title %}{% endblock title %}</title>
</head>

En nuestra template base empezamos por incluir los scripts de jquery y de materialize junto con los estilos del mismo. Si no tienes conexión a Internet te recomiendo que los descargues y lo incluyas manualmente, es por ello que necesitas tener bien configurada la ruta de todos estos archivos estáticos (CSS, JS e imágenes).

Hemos definido ademas en la ultima linea una etiqueta block, esta etiqueta nos permite definir “huecos” (por decirlo de alguna forma), que luego serán “llenados” en otras templates, mas adelante veremos de qué va esto.

En el mismo archivo (base.html) coloca lo siguiente:


  <body style="display:flex; min-height:100vh; flex-direction:column;">
<header>
<!-- Menu principal -->
<nav>
  <div class="nav-wrapper teal darken-1">
    <a href="{% url 'sitio:index' %}" class="brand-logo">Bienvenido</a>
  <a href="#" data-activates="mobile-demo" class="button-collapse"><i class="material-icons">menu</i></a>
  <ul class="right hide-on-med-and-down">
    <li class="activate"><a href="{% url 'sitio:index' %}">Inicio</a></li>
    <li><a href="{% url 'sitio:crear' %}">Crear Libro</a></li>
  </ul>
  <ul id="mobile-demo" class="side-nav">
    <li class="activate"><a href="{% url 'sitio:index' %}">Inicio</a></li>
    <li><a href="{% url 'sitio:crear' %}">Crear Libro</a></li>
  </ul>
  </div>
</nav>
<!--fin menu principal -->
</header>

Acá hemos definido el menú principal de nuestra aplicación, usando etiquetas url indicamos la ruta a seguir luego de que el usuario de clic en cada enlace. Por último, terminamos nuestro archivo base.html con lo siguiente:


<main style="flex: 1 0 auto;">
  <div class="container">
  <div class="divider"></div>
   <div class="section">
     <h4>{% block section_name %}{% endblock section_name %}</h4>
     <p>{% block section_description %}{% endblock section_description %}</p>
   </div>
  {% block main %}
  <!--Contenido especifico de cada template-->
  {% endblock main %}
  </div>  
</main> 
<!--footer inicio -->
<footer class="page-footer teal darken-1">
  <div class="container">
    <div class="row">
      <div class="col l6 s12">
        <h5 class="white-text">Mi primera aplicación en Django</h5>
          <p class="grey-text text-lighten-4">La Biblioteca</p>
      </div>
    </div>
  </div>
  <div class="footer-copyright">
    <div class="container">
      © 2016 Copyright Todos los derechos reservados
    </div>
  </div>
</footer>
<!--  footer fin -->
<!-- Configuracion e inicializaciones Javascript -->
<script>
//Menu desplegable, necesario para activar el menu 
//en un tamanio responsivo
$(document).ready(function(){
  $('.button-collapse').sideNav();
});
</script>   
</body>
</html>

Las etiquetas block main serán en donde se encontrará todo el contenido dependiendo de la template, usamos un block section para un pequeño encabezado asi como otra etiqueta block para una descripción. Cabe agregar que main, section, section_description pueden ser cambiados por otros nombres; lo que importa es que las etiquetas comienzen con block y finalicen con endblock. Por último creamos un pequeño footer.

Ya hemos establecido nuestra base, de donde heredarán todas nuestras templates. Ahora solo queda definir lo específico de cada template. Vamos primero con allbooks.html, abrelo y coloca lo siguiente:


  
<!--Heredamos de base.html-->
{% extends "biblioteca/base.html" %}
<!--cargamos archivos estaticos-->
{% load staticfiles %}
<!--llenamos los huecos definidos en base.html-->
{% block title %}Inicio{% endblock title %}
{% block section_name %}Inicio{% endblock section_name %}
{% block section_description %}Catálogo de libros disponibles{% endblock section_description %}

{% block main %}
<div class="row">
{% for libro in object_list  %}<!--object_list es el nombre que django da por default a la lista donde estan los objetos (en este caso libros) -->
  <!-- los libros de la bd se muestran en una tarjeta de imagen de materialize -->
  <div class="col s12 m4">
    <div class="card blue-grey lighten-3">
      <div class="card-image">
        <img src="{% static libro.portada %}" width="100px" height="200px">
      </div>
      <div class="card-content">
        <ul>
          {% for autor in libro.autores.all %}   
          <li><strong>Título:</strong>{{ libro.titulo }}</li>
          <li><strong>Autor(es): </strong>{{ autor.nombre }} {{ autor.apellido }}</li>
          {% endfor %}<!--fin for interno-->
          <li><strong>Editor: </strong>{{ libro.editor }}</li>
          <li><strong>Fecha de publicación: </strong>{{ libro.fecha_publicacion }}</li>
        </ul>
      </div>
      <div class="card-action">
        <a href="{% url 'sitio:update' libro.pk %}"><span class="blue-grey-text">Editar libro</span></a>
        <a href="{% url 'sitio:detail-book' libro.pk %}"><span class="blue-grey-text">Ver Detalles</span></a>
      </div>
    </div>
  </div>
{% empty %}<!-- Que pasa cuando no hay libros? -->
<div class="row">
  <div class="col s12 m6 l6 offset-l3">
    <div class="card blue-grey lighten-3">
      <div class="card-content black-text">
        <span class="card-title black-text"><strong>No hay libros disponibles</strong></span>
        <p>En este momento no hay libros en la plataforma, pero puedes crear uno si quieres.</p>
      </div>
      <div class="card-action">
        <a href="{% url 'sitio:crear' %}"><span class="blue-grey-text">Crear un libro</span></a>
      </div>
    </div>
  </div>
</div>
{% endfor %} <!--fin for externo--> 
</div> 
{% endblock main %}

Lo primero que hacemos es heredar del archivo base.html, luego cargamos los archivos estáticos que en nuestro caso serán solo imágenes (las portadas de nuestro libro). Para ello hacemos uso del tag load staticfiles, tambien hemos usado un par de etiquetas nuevas, la etiqueta for y la etiqueta empty.

La etiqueta for nos permite recorrer todos los elementos de una lista, en nuestro caso object_list es la lista de libros que recorrerá. Por cada libro dentro de la lista mostraremos su nombre, portada, autores, editor y fecha de publicación. Por otra parte, la etiqueta empty nos permite controlar lo que pasa cuando la lista a recorrer está vacia, en nuestro caso solo mostraremos un mensaje cuando eso suceda.

Miremos como va quedando, corre el servidor, ya sabes python manage.py runserver (si como yo, tienes el proyecto sobre virtualenv, primero actívalo). Si colocas http://localhost:8000/biblioteca/ debes ver lo siguiente.

index

Mas abajo hemos puesto dos links que redirigen a las templates de edición y de detalles, cada una tiene lo siguiente:

  • Template de detalles (detailbook.html):

{% extends "biblioteca/base.html" %}
{% load staticfiles %}
{% block title %}Detalles{% endblock title %}
{% block section_name %}Detalles{% endblock section_name %}
{% block main %}
  <div class="row">
    <div class="col s12 l4 m5">
      <div class="card hoverable" width="100px" height="200px">
            <div class="card-image">
              <img src="{% static object.portada %}">
            </div>
            <div class="card-content blue-grey lighten-5">
              <span class="card-title" style="color:#546e7a;"><strong>{{  object.titulo }}</strong></span>
            </div>  
        </div>
    </div>
    <div class="col s12 l8 m7">
      <table class="hoverable">
        <thead class="hoverable blue-grey lighten-3">
          <tr>
            <th>Título</th>
            <th>Autor</th>
            <th>Editor</th>
            <th>Fecha de publicación</th>
          </tr>
            </thead>
            <tbody class="blue-grey lighten-5">
              <tr>
                <th>{{object.titulo }}</th>
                <th>{{ object.autores.get }}</th>
                <th>{{ object.editor }}</th>
                <th>{{ object.fecha_publicacion }}</th>
              </tr>
            </tbody>
      </table>
      <div class="row">
        <div class="col l12 m12 s12 ">
          <h3>Sinopsis</h3>
          <p>{{ object.sinopsis }}</p>  
        </div>
      </div>
        <a href="{% url 'sitio:delete' object.pk %}" class="btn-floating waves-effect waves-light tooltipped teal darken-1" data-tooltip="Eliminar Libro"data-position="right"><i class="material-icons">delete</i></a>
    </div>
  </div>
{% endblock main %}

Si das click en detalles, verás lo siguiente:

detalles

Acá no hay mucho por decir, simplemente hemos organizado la información del libro en un tabla y usado la grid de materialize para darle un mejor aspecto. Tal vez te preguntes que diablos es object.autores.get, como sabes cada Libro tiene uno o varios Autores, este a su vez es un modelo y podemos usar la API de modelos de Django para traer todos los autores asociados a un libro en particular, eso es lo que hemos hecho.

Al final hemos puesto un link para eliminar el libro, este nos redirige a la vista de confirmación de eliminar.

  • Template de edición (uc_libro.html):
  
{% extends "biblioteca/base.html" %}
{% block title %}Sitio Lecutra{% endblock title %}
{% block section_name %}¡ Vamos a hacerlo !{% endblock section_name %}
{% block main %}
  <form action="" enctype="multipart/form-data" method="POST">{% csrf_token %}
    <div class="row">
      <div class=" col s12 m8 offset-m6 l6 offset-l3">
        <div class="card  blue-grey lighten-3">
          <div class="card-content">
            <table>
              {{ form.as_table }} 
            </table>
          </div>
          <div class="card-action">
            <button class="btn waves-effect waves-light  blue-grey" type="submit" name="crear" value="crear">Enviar</button>  
          </div>
        </div>
      </div>
    </div>        
  </form>
  <script>
    // Para el correcto funcionamiento de los desplegables presentes en el formulario select
    // se debe de incluir este script
    $(document).ready(function() {
      $('select').material_select();
    });
  </script>
{% endblock main %}

Esta template también es usada en la creación de libros como ya vimos en la entrada pasada, Django renderiza esta template dependiendo de la acción que se vaya a realizar (a veces parece inteligente :o). Acá tampoco hay mucho por decir, simplemente hemos cambiado la apariencia heredando de nuestra base y mejorando el aspecto del formulario.

edicion

El pequeño script de mas abajo se debe colocar para que las listas desplegables funcionen correctamente.

Por último la template de confirmación de borrado queda como sigue

  • Confirmación de borrado (libro_confirm_delete.html):
  
{% extends "biblioteca/base.html" %}
{% block section_name %}Confirmación de borrado{% endblock section_name %}
{% block main %}
  <form action="" method="POST">{% csrf_token %}
    <div class="row">
      <div class=" col  s12 m8 offset-m6 l6 offset-l3">
        <div class="card  blue-grey lighten-3">
          <div class="card-content">
            <span class="card-title black-text"><strong>Confirmación</strong></span>
            <p>¿Deseas borrar el libro {{ libro.titulo }}?</p>
          </div>
          <div class="card-action">
            <button class="btn waves-effect waves-light  blue-grey" 
            type="submit">Confirmar</button>
            <a class="btn waves-effect waves-light  blue-grey white-text"
             href="{% url 'sitio:index' %}">Cancelar</a>
          </div>      
        </div>
      </div>
    </div>
  </form>       
{% endblock main %}

Cuando das clic en el botón de borrar libro, te redirige a la siguiente vista de confirmación:

confirm

Todo luce mejor ahora ¿no crees?, la verdad que Materialize es un lindo framework, aunque no hemos usado todos sus componentes nos ha cambiado mucho el look and feel de nuestra app de forma rápida y sencilla. El código de todas las entradas estará alojado en Github en este repositorio, espero que hayas aprendido un poco acerca de las funcionalidades de Django y te animes a seguir por tu cuenta. No siendo más nos vemos en una próxima entrada.

print("Hasta pronto")
aplicacion, Django 1.8, Biblioteca
comments powered by Disqus