Django lo hizo un mago: paginación y detalle


En el artículo anterior Django lo hizo un mago creamos un entorno básico con un listado de posts para un blog. En esta ocasión vamos a añadir más funcionalidad.

En concreto, veremos cómo visualizar cada post (lo que llamaremos detalle), y añadiremos paginación.

Pero antes un extra: Añadir elementos aleatorios de pruebas fácilmente con sampledatahelper

Cómo leer este artículo

Hay tres maneras de leer este artículo.

  1. Continuando tras la lectura de Django lo hizo un mago
  2. Descargándose el tag 0.1.0 del repositorio GitHub git@github.com:magmax/django-blog.git
  3. Viendo los resultados descargándose el tag 0.2.0 del repositorio GitHub git@github.com:magmax/django-blog.git

Acordáos de crear el entorno Virtualenv e instalar las dependencias.

Y dicho esto, vamos al lío.

Documentos a cholón

Personalmente encuentro bastante fastidioso tener que escribir muchos artículos, así que vamos a instalarnos una librería llamada sampledatahelper y a configurarla para que nos genere documentos aleatorios.

Instalamos el paquete:

(env)$ pip install django-sampledatahelper==0.2.1

Instalamos la aplicación, añadiéndola a la variable INSTALLED_APPS del archivo app/settings.py:

python-django/002/app/app/settings.py

INSTALLED_APPS = (
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'blog',
    'sampledatahelper',
)

Y en ese mismo archivo añadimos la configuración necesaria:

python-django/002/app/app/settings.py

SAMPLEDATAHELPER_MODELS  = [
    { 'model': 'blog.Post', 'number': 100, },
    { 'model': 'blog.Tag', 'number': 5, },
]

y ejecutamos:

python manage.py sampledatafiller

Y tendremos escritos 100 posts pertenecientes a 5 categorías diferentes. Podéis ejecutar varias veces para generar más o jugar con los parámetros :D

Paginación

Con tantos documentos no hay quien lea nada.

Vamos a paginar. Para ello, editamos nuestro controlador, que en Django se llama vista(archivo blog/views.py):

from django.views.generic.list import ListView

from . import models


class PostListView(ListView):
    model = models.Post
    paginate_by = 8

Y con eso se nos reducirán los documentos a 8. Ahora editamos la plantilla, para saltar al anterior y al siguiente:

python-django/002/app/templates/blog/post_list.html

<div>
    {% if page_obj.has_previous %}
      <a href="{% url 'home' %}?page={{ page_obj.previous_page_number }}">
        <span>&laquo; Previous</span>
      </a>
    {% else %}
      <span>&laquo; Previous</span>
    {% endif %}

      <span> | <span>

    {% if page_obj.has_next %}
      <a href="{% url 'home' %}?page={{ page_obj.next_page_number }}">
        <span>Next &raquo;</span>
      </a>
    {% else %}
      <span>Next &raquo;</span>
    {% endif %}
</div>

No creo que tengáis problemas en entenderlo, más allá de encontrar el truco de magia: Django nos ha inyectado una variable llamada page_obj. Esto es lo que se denomina contexto y que veremos en mayor detalle en el artículo de plantillas y estáticos.

Qué difícil ¿no?

Si os sentís artistas podéis darle un estilillo más chulo. De todas maneras hablaremos más en algún artículo dedicado a las plantillas y estáticos.

Viendo los posts

Ha llegado el momento de ver el contenido de los posts. Esto tiene bastantes modificaciones. Vayamos por partes:

El controlador

No vamos a modificar el modelo, así que vamos directamente con el controlador (archivo blog/views.py, acordaos):

python-django/002/app/blog/views.py

from django.views.generic.list import ListView
from django.views.generic.detail import DetailView

from . import models


class PostListView(ListView):
    model = models.Post
    paginate_by = 8


class PostDetailView(DetailView):
    model = models.Post

Poco que explicar aquí: importamos django.views.generic.detail.DetailView y creamos una clase de ese tipo, asociándola con nuestro modelo blog.models.Post.

El enrutador

python-django/002/app/app/urls.py

from django.conf.urls import patterns, include, url

from django.contrib import admin
admin.autodiscover()

from blog import views


urlpatterns = patterns('',
    url(r'^$', views.PostListView.as_view(), name='home'),
    url(r'^post/(?P<pk>\d+)$', views.PostDetailView.as_view(), name='post_detail'),
    url(r'^admin/', include(admin.site.urls)),
)

Sólo he añadido una línea, la segunda url. Aquí estoy usando una expresión regular((?P<pk>\d+)) que puede parecer compleja, pero no lo es. Tan sólo cojo uno o más números y le doy un nombre: pk (de primary key). Utilizo esta expresión regular para componer las URLs que identificarán el detalle de un post, asociándolo con la clase que creamos en el paso anterior. Además, le doy un nombre.

Enlazando

Ahora podemos enlazar en la lista de posts, templates/blog/post_list.html, del que me salto la parte de la paginación:

python-django/002/app/templates/blog/post_list.html

1
2
3
4
5
<ul>
  {% for post in post_list %}
  <li><a href="{% url 'post_detail' post.id %}">{{ post.title }}</a></li>
  {% endfor %}
</ul>

La vista

Si lanzamos el servidor y pulsamos sobre estos enlaces, nos dará un error porque no encuentra el archivo blog/post_detail.html. Vamos a crearlo:

python-django/002/app/templates/blog/post_detail.html

<h1>{{ post.title }}</h1>

<div>
  {{ post.teaser }}
</div>

<hr />

<div>
  {{ post.body }}
</div>

<a href="{% url 'home' %}">Post list</a>

Y, ahora sí, podemos ver el detalle.

Conclusión

Si hacéis un show de los cambios de esta versión, descubriréis que los cambios que hemos hecho hoy son muy pequeños:

6 files changed, 49 insertions(+), 1 deletion(-)

Creo que esto explica el motto de Django:

Django makes it easier to build better Web apps more quickly and with less code.

Si os ha gustado, podéis continuar con Django lo hizo un mago: plantillas y contextos, donde mejoraremos el aspecto dándole una calidad profesional con muy poco esfuerzo.


Comentarios

Comments powered by Disqus