Dos clases que sustituyen las dos vistas que ya tienes, la paginación que sale sola, y los nombres que hay que adivinar.
⏱ 15 min de lectura
Las dos vistas que escribiste en la lección 16 hacen lo que hacen el noventa por ciento de las páginas de cualquier sitio: enseñar una lista y enseñar un elemento. Django trae las dos hechas.
Lo interesante no es que ahorren líneas, que también. Es que traen resuelto lo que se te olvida: la paginación, el 404 cuando no existe, el filtrado por parámetros de la dirección. Y a cambio piden aprenderse unos cuantos nombres que no están escritos en ninguna parte de tu código, que es la pega de la lección anterior en su forma más concreta.
Falta la plantilla, y ahí está el primer nombre inventado. ListView busca aplicacion/modelo_list.html, todo en minúsculas, con el nombre del modelo pegado al _list. No lo has escrito en ningún sitio: sale de model.
Con la plantilla puesta:
Salida en pantalla
Claves: object_list=5 contacto_list=5
Y ahí está el segundo: los objetos llegan al contexto con dos nombres, object_list y contacto_list, el segundo formado igual que la plantilla. El genérico sirve para escribir plantillas reutilizables y el específico se lee mejor.
Cuatro atributos y dos métodos, y ya es una lista paginada con buscador. Los dos métodos son los que se sobrescriben siempre:
get_queryset decide qué objetos. Empieza llamando al padre, que devuelve model.objects.all(), y encadena lo tuyo. Es el sitio del select_related, del filter y del order_by.
get_context_data añade lo que no son los objetos: el término buscado, los grupos para un desplegable, lo que haga falta. Con el super() de la lección anterior, que no es opcional.
Y fíjate en self.request. Dentro de una vista de clase, la petición está en self, no en un parámetro. Con ella se llega a self.request.GET, self.request.user y self.kwargs, que es donde están los trozos capturados de la dirección.
paginate_by mete tres cosas en el contexto: paginator, page_obj y is_paginated. Medidas:
Salida en pantalla
PAGINA: 1 de 3 total=5 hay_paginas=True
ANT: no SIG: 2
NOMBRES: Ana Ruiz; Luis Pardo;
PAGINA: 2 de 3 total=5 hay_paginas=True
ANT: 1 SIG: 3
NOMBRES: Por id; Sin grupo;
La página se elige con un parámetro en la dirección, ?page=2, y una que no existe da 404:
Salida en pantalla
?page=99 -> 404
Lo que casi siempre se escribe igual, y conviene tenerlo en un trozo incluido de los de la lección 18:
contactos/templates/contactos/_paginacion.html
{% if is_paginated %}<nav class="paginacion">{% if page_obj.has_previous %}<a href="?page={{ page_obj.previous_page_number }}">Anterior</a>{% endif %}<span>Página {{ page_obj.number }} de {{ paginator.num_pages }}</span>{% if page_obj.has_next %}<a href="?page={{ page_obj.next_page_number }}">Siguiente</a>{% endif %}</nav>{% endif %}
Busca contactos/contacto_detail.html y deja el objeto con los dos nombres, igual que antes. El get_object_or_404 está dentro: si no existe, 404.
Lo único que hay que saber es de dónde saca el objeto, porque depende de lo que capture la ruta:
En la ruta
Qué hace
<int:pk>
Busca por clave primaria
<slug:slug>
Busca por el campo slug del modelo
<slug:slug> con slug_field = 'url'
Busca por el campo url
Si la ruta no captura ni pk ni slug, la vista lanza una excepción explicando que no sabe qué buscar. Y si hace falta otra cosa —el objeto de la persona que ha entrado, por ejemplo—, se sobrescribe get_object.
DetailView también tiene get_queryset, y ahí es donde va el select_related y, sobre todo, el filtro de seguridad: