Django, vistas basadas en clases (II): Detail View

Hoy vamos a hablar de la vista basada en clases más fácil y simple de usar, Detail View. Esta vista nos permite acceder a un objeto en base de datos y mostrarlo con nuestra plantilla html en, literalmente, 2 líneas. Además tendrá comportamientos básicos asociados. Por ejemplo, si no encuentra el objeto, devolverá un error 404. O si intentamos acceder con método POST devolverá un error 405, method not allowed.

Un código básico para una Detail View podría ser así:

from django.views.generic import DetailView

from poster.models import Post


class PostDetailView(DetailView):
    model = Post

Lo único realmente necesario en una Detail View es el modelo de datos que queremos utilizar. En este caso Post. Ahora añadimos una línea a nuestro urls.py:

url(r'^(?P<slug>[-_\w]+)/$', 
    PostDetailView.as_view(), 
    name='post-detail'),

En la url podemos utilizar slug, que es un campo basado en texto y buscará por un atributo con el mismo nombre, slug (aunque podemos personalizarlo). Este parámetro en la url será el usado para elegir el objeto a mostrar desde la base de datos. También podemos utilizar pk y traerá el objeto en base de datos que tenga la misma clave primaria. Por defecto, la plantilla será '<nombre_app>/<nombre_modelo><sufijo_vista>.html'. En este caso, 'poster/post_detail.html'. ¡Ojo! Los guiones serán reemplazados con barras bajas. En la plantilla el objeto será accesible usando el nombre del modelo (en este caso, Post, tendremos disponible post, que también podemos personalizar). Fácil ¿verdad?

¿Y si queremos algo más personalizado? Por ejemplo, si queremos elegir el nombre de nuestra plantilla, bastará con añadir un atributo template_name a la clase. ¿Y si queremos que busque el objeto utilizando el título en lugar de slug? Añadimos el atributo slug_field. El código de la vista quedaría así:

class PostDetailView(DetailView):
    model = Post
    template_name = 'poster/mi_plantilla.html'
    slug_field = 'title'

¿Podemos personalizar más cosas? Claro que sí. Por ejemplo, en el caso de un blog, sería interesante mantener un campo de si una entrada ha sido publicada o no. Sería interesante restringir el acceso de la DetailView a esas entradas y no permitir el acceso hasta que no estén publicadas. ¿Cómo lo hacemos? Vamos a modificar la consulta a base de datos que será realizada. Para ello vamos a sobrescribir el método get_queryset proporcionado por la clase DetailView de la siguiente forma:

class PostDetailView(DetailView):
    model = Post
    template_name = 'poster/mi_plantilla.html'
    slug_field = 'title'

    def get_queryset(self):
        query = super(PostDetailView, self).get_queryset()
        return query.filter(published=True)

En primer lugar llamamos al método get_queryset de la superclase. A continuación simplemente tenemos que devolver la query añadiendo un filtro para asegurar que el objecto ha sido publicado. Así si un objeto existe con el slug especificado pero no ha sido publicado la aplicación devolverá un error 404.

De esta misma forma podemos modificar absolutamente todo el comportamiento de la vista. Si quieres ver los métodos disponibles en la vista, visita la documentación oficial de DetailView. Si tienes dudas, comentarios o quieres alguna explicación adicional, comenta aquí abajo e intentaré ampliar esta entrada. ¡Saludos!