En este sexta entrega veremos cómo podemos mostrar listad de objetos, algo que normalmente va muy ligado al flujo de trabajo asociado al CRUD.

Para mostrar listados (paginados o no) Django nos proporciona la clase ListView que podemos encontrar en django.views.generic.list. Esta clase és hija de MultipleObjectTemplateResponseMixin y de BaseListView. Esta última clase es la que hace el grueso del trabajo, ya que es hija de MultipleObjectMixin y de View implementando el método get.

Como en el caso de las vistas oreintadas al trabajo con CRUD también en este caso tenemo una plantilla por defecto, formada por el nombre del modelo y el sufijo '_list'.

Lo que más nos va a interesar normalmente son los métodos y atributos de MultipleObjectMixin. Para empezar no nos complicaremos mucho la vida, mostraremos la lista de los objetos que hemos creado en el post anterior:

A url.py:

url(r'^tutorial/sample/list/$', SampleListView.as_view(),
    name='tutorial_list_sample'),

y al views.py podríamos escribir:

from django.views.generic.list import ListView

class SampleListView(ListView):
    model = Sample

Nos queda por definir la plantilla, que tendrá como nombre sample_list.html, pero antes veamos qué variables se van a pasar al contexto de la vista. A saber:

  • paginator
  • page_obj
  • is_paginated
  • object_list

En contenido de las variables pueden cambiar en función de si tenemos paginación o no, pero las variables serán estas (a no ser que añadamos más, por supuesto). La que más nos interesa ahora es object_list, que contienen la lista de objetos del modelo. De manera que podemos crear una plantilla del tipo:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" 
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
    <title>test</title>
</head>
<body>
    <h1>Sample Detail List</h1>
    <table border="1">

    {% for sample in object_list %}
    <tr>
        <td><a href="{% url tutorial_update_sample sample.slug %}">
            {{sample.slug}}</a><td/>
        <td>{{sample.name}}<td/>
        <td>{{sample.ammount}}<td/>
        <td>{{sample.comments}}<td/>
        <td>
            <a href="{% url tutorial_delete_sample sample.slug %}">Delete</a>|
            <a href="{% url tutorial_create_sample %}">Create</a>
        </td>
        </tr>
    {% endfor %}
    </tr>
    </table>
</body>
</html>

Como en otros ejemplos de plantilla no estoy utiliznando la herencia, el html es muy simple, etc. Lo importante aquí es ver cómo podemos recorrer la lista de objetos para contruir una tabla, y con lo que ya vmos de CRUD podemos enlazar cada objeto con su mantenimiento correspondiente.

A la hora de crear, editar o borrar un registro, podemos elegir entre mostrar dicho registro (si lo tenemos) o bien ir al listando. En este último caso bastaría añadir

success_url = reverse_lazy('tutorial_list_sample')

a las vista que nos interesen.

Paginación

Normalmente tendremos bastante resultados y es común que los listados se muestren paginados. Para poder hacer esto de manera automática hemos de añadir el atributo paginate_by a la clase, que nos determinará el número máximo de elementos que se mostrarán por página.

 class SampleListView(ListView):
    model = Sample
    paginate_by = 2

Si modificamos así nuestro código y no modificamos la plantilla sólo veremos dos registros. Nos falta todavía añadir los controles de página anterior y página siguiente. Hay numerosos ejemplos de cómo mostrar la paginación, pero por sencillez nos bastaría:

{% if is_paginated %}
        {% if page_obj.has_previous %}
          <a href="{% url tutorial_list_sample %}?page={{ page_obj.previous_page_number }}">
             Previous</a>
        {% endif %}
            <!-- current page -->
                Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}.
            <!-- end current page -->
        {% if page_obj.has_next %}
         <a href="{% url tutorial_list_sample %}?page={{ page_obj.next_page_number }}">
                Next</a>
         {% endif %}
{% endif %}

Recodemos que hemos deicho que al contexto se pasaba page_object. Fijémonos como esta variable se emplea para acceder a las propiedades de la página actual, el número de páginas totales, saber si una página tiene una página anterior o una posterior y el número que le corresponde.

También hemos hecho uso de is_paginated de manera que no mostraremos la función de paginación si sólo hay una página.

¡Y ya está! Sencillo, ¿verdad?