Django, vistas basadas en clases (III): List View

Tras las Detail View, las vistas más simples de utilizar son las List View. Este tipo de vista nos ayudará a gestionar listas de objetos traídos de la base de datos. Al igual que las anteriores, podemos usarlas con sólo 2 líneas de código. Además, nos va a ayudar crear paginación, seleccionar tamaño de las páginas y demás.

El código más básico para una List View podría ser así:

from django.views.generic import ListView

from poster.models import Post


class PostListView(ListView):
    model = Post

El único elemento imprescindible para una List View es el modelo de datos que queremos utilizar. Al igual que el caso anterior utilizaremos Post. Añadimos la vista a nuestro urls.py:

url(r'list/^', PostListView.as_view(), name='post-list'),

Como podéis ver la url es muy sencilla. No contiene ningún parámetro. La plantilla por defecto será '<nombre_app>/<nombre_modelo><sufijo_vista>.html', en este caso, 'poster/post_list.html', aunque podemos añadir un template_name igual que hicimos anteriormente en el tutorial sobre Detail View. En la plantilla podremos acceder al listado de objetos utilizando object_list o <nombre_modelo>_list, en este caso post_list, aunque podemos personalizarlo añadiendo un context_object_name del mismo modo que hacemos con las plantillas.

Esta vista, hasta el momento, no está proporcionando todos los objetos Post de la base de datos. ¿Y si queremos algo más personalizado? Por ejemplo, si queremos filtrar los posts para que solo se muestren los que ya han sido publicados, podemos añadir un filtro sobrescribiendo el método get_queryset de la siguiente forma:

class PostListView(ListView):
    model = Post

    def get_queryset(self):
        queryset = super(PostListView, self).get_queryset()
        return queryset.filter(published=True)

¿Os suena familiar? El filtro que acabamos de añadir es exactamente igual que el que creamos para filtrar las Detail View.

¿Como añadimos paginación? No puede ser más sencillo. Tan sólo tenemos que añadir un atributo de clase paginate_by a la vista indicando el número de elementos por página que queremos mostrar, de la siguiente manera:

class PostListView(ListView):
    model = Post
    paginate_by = 10

    def get_queryset(self):
        queryset = super(PostListView, self).get_queryset()
        return queryset.filter(published=True)

En este caso cada página contendrá 10 elementos. Para acceder a las diferentes páginas basta con añadir a la url ?page=2, en este caso a la segunda página. Podemos utilizar el parámetro last para acceder a la última página así: ?page=last. Si intentamos acceder a una página que no existe, la aplicación devolverá un error 404.

Además, nos proporciona un objeto page_obj para que podamos gestionar nuestras páginas correctamente, por ejemplo, para crear enlaces a la página siguiente o a la página anterior. Este objeto nos proporciona métodos como has_next, has_previous, previous_page_number o next_page_number. Con ellos podremos construir fácilmente enlaces a las diferentes páginas de nuestra lista. Por ejemplo, podríamos añadir a nuestra plantilla algo así:

{% if page_obj.has_previous %}
        <a href="{% url 'post-list' %}?page={{ page_obj.previous_page_number }}">
        Ir a página {{ page_obj.previous_page_number }}
        </a>
{% endif %}
        La página actual es {{ page_obj.number }}
{% if page_obj.has_next %}
        <a href="{% url 'post-list' %}?page={{ page_obj.next_page_number }}">
        Ir a página {{ page_obj.next_page_number }}
        </a>
{% endif %}

Modificar otros comportamientos de la vista es igual de sencillo. Si queréis ver los método disponibles de la vista, echa un vistazo a la documentación oficial de ListView. Para más información sobre la paginación, consulta la documentación del mixin MultipleObjectMixin.

Si tienes dudas, comentarios o quieres alguna explicación adicional, comenta aquí abajo e intentaré ampliar esta entrada. ¡Disculpas por haber tardado tanto en escribir este post! ¡Saludos!