Django 2

Django 2

Antonio Melé

Copyright © Packt Publishing 2018. First published in the English language under the title Django 2 by Example – (9781788472487).

Primera edición original publicada en inglés por Packt Publishing Ltd. con el título: Django 2 by Example, © 2018 Antonio Melé

Título de la edición en español: Django 2

Primera edición en español, año 2020

© 2020 MARCOMBO, S.L.

www.marcombo.com

Traducción: Aitor Ruiz Fajardo y Jaime Fernández Martín

Correctora: Nuria Barroso

Directora de producción: M.a Rosa Castillo

«Cualquier forma de reproducción, distribución, comunicación pública o transformación de esta obra solo puede ser realizada con la autorización de sus titulares, salvo excepción prevista por la ley. Diríjase a CEDRO (Centro Español de Derechos Reprográficos, www.cedro.org) si necesita fotocopiar o escanear algún fragmento de esta obra».

ISBN: 978-84-267-2866-1

Producción del ebook: booqlab.com

Para mi hermana

Acerca del autor

Antonio Melé es CTO de Exo Investing y fundador de Zenx IT. Antonio ha desarrollado proyectos Django desde 2006 para clientes de distintas industrias. También ha trabajado como CTO y consultor de tecnología para múltiples startups y ha gestionado equipos de desarrollo tecnológico para negocios digitales. Antonio es ingeniero superior en informática por la Universidad Pontificia Comillas. Su padre le inspiró su pasión por los ordenadores y la programación.

Acerca de los traductores

Aitor Ruiz Fajardo es Senior Software Development Engineer en Voysis en Dublín. Aitor ha trabajado principalmente en el desarrollo de arquitecturas y aplicaciones Java en proyectos para múltiples empresas en distintos sectores. Entusiasta del software y la gestión desde bien pequeño, Aitor es ingeniero superior en informática por la Universidad Carlos III de Madrid.

Jaime Fernández Martín es un consultor digital especializado en la implantación de software para negocios. Lleva trabajando con Python desde hace más de 10 años y ha sido coorganizador de Python-Madrid. Ha trabajado en diferentes sectores como: biotecnología, aeronáutica, telecomunicaciones, energía y financiero, aplicando la tecnología como medio para mejorar los procesos de negocio. Jaime es ingeniero informático por la Universidad Politécnica de Madrid, con un máster en inteligencia artificial por la misma universidad, y un MBA por CESMA Business School.

Contenido

Prefacio

1. Crear un blog

Instalar Django

Crear un entorno de desarrollo Python

Instalar Django mediante pip

Crear el primer proyecto

Arrancar el servidor de desarrollo

Configuración del proyecto

Proyecto y aplicaciones

Crear una aplicación

Esquema de datos del blog

Activar la aplicación

Crear y activar migraciones

Sitio de administración para los modelos

Crear un superusuario

El sitio de administración de Django

Añadir modelos al sitio de administración

Personalizar la vista de modelos

Trabajando con QuerySets y Managers

Crear objetos

Actualizar objetos

Recuperar objetos

Valiéndose del método filter()

Uso de exclude()

Uso de order_by()

Eliminar objetos

Cuándo se evalúan los QuerySets

Crear gestores de modelos

Elaborar listas de detalle y listado

Creación de vistas de detalle y listado

Añadir patrones de URL para las vistas

Crear plantillas para vistas

Añadir paginación

Usar vistas basadas en clase

Resumen

2. Funciones avanzadas del blog

Compartir artículos por email

Crear formularios en Django

Usar formularios en vistas

Enviar emails con Django

Renderizar formularios en plantillas

Crear un sistema de comentarios

Crear formularios de un modelo

Funcionamiento de ModelForms en vistas

Añadir comentarios a la plantilla de detalle de artículos

Añadir funcionalidad de etiquetado

Recuperar artículos por similitud

Resumen

3. Extensiones para el blog

Crear filtros y etiquetas de plantilla personalizados

Crear etiquetas de plantilla personalizadas

Crear filtros de plantilla personalizados

Añadir un mapa de sitio

Sistema de feeds

Crear un sistema de búsqueda para el blog

Instalar PostgreSQL

Búsquedas sencillas

Búsqueda sobre múltiples campos

Construir una vista de búsqueda

Lematización y resultados por semejanza

Orden de relevancia en búsquedas

Búsquedas por similitud de trigrama

Otros motores de búsqueda de texto completo

Resumen

4. Construir una aplicación social

Crear un proyecto de red social

Iniciar el proyecto de red social

Utilizar el sistema de autenticación de Django

Crear una vista de inicio de sesión

Utilizar las vistas de autenticación de Django

Vistas para iniciar y cerrar sesión

Vistas para modificar la contraseña

Vistas para restaurar la contraseña

Registro y perfiles de usuario

Registro de usuarios

Extender el modelo de usuario

Usar un modelo de usuario personalizado

Utilizar el sistema de mensajes

Crear un backend de autenticación personalizado

Añadir autenticación social a la web

Autenticación mediante Facebook

Autenticación mediante Twitter

Autenticación mediante Google

Resumen

5. Compartir contenido en su web

Crear un sitio web para compartir imágenes

Construir el modelo imagen

Crear relaciones muchos a muchos

Registrar el modelo imagen en el portal de administración

Enviar contenido desde otros sitios web

Validar campos de formulario

Sobrescribir el método save() de un ModelForm

Elaborar un bookmarklet con jQuery

Crear una vista de detalle para las imágenes

Crear miniatura de imágenes utilizando sorl-thumbnail

Añadir acciones AJAX con jQuery

Cargar jQuery

Falsificación de petición en sitios cruzados con AJAX

Realizar peticiones AJAX con jQuery

Crear decorados para vistas

Añadir paginación AJAX a listados

Resumen

6. Seguimiento de acciones de usuario

Construir un sistema de seguidores

Crear relaciones muchos a muchos a través de un model intermedio

Crear vistas de listado y detalle para perfiles de usuario

Construir una vista AJAX para seguir usuarios

Construir un feed de actividad

Utilizar el sistema de contenttypes

Añadir relaciones genéricas a modelos

Evitar registrar acciones duplicadas

Añadir acciones de usuario al registro de actividad

Mostrar el registro de actividad

Optimizar QuerySets que involucran objetos relacionados

Utilizar select_related()

Utilizar prefetch_related()

Crear plantillas parea acciones

Usar señales para desnormalizar conteos

Trabajar con señales

Clases de configuración de aplicaciones

Usar Redis para contabilizar visualizaciones

Instalar Redis

Usar Redis con Python

Almacenamiento total de visualizaciones en Redis

Almacenar un ranking en Redis

Siguientes pasos con Redis

Resumen

7. Construir una tienda online

Crear un proyecto de tienda online

Modelo de catálogo de productos

Registrar el catálogo en el panel de administración

Construir vistas de catálogo

Crear plantillas de catálogo

Construir un carro de la compra

Sistema de sesiones en Django

Configuración de sesión

Expiración de sesión

Almacenar carros de compra en sesión

Vistas para el carro de la compra

Añadir elementos al carro

Construir una plantilla para mostrar el carro

Añadir productos al carro

Modificar la cantidad de producto en el carro

Crear un procesador de contexto para el carro actual

Registrar órdenes de clientes

Crear modelo de órdenes

Añadir los modelos de órdenes al panel de administración

Crear órdenes de clientes

Ejecutar tareas asíncronas con Celery

Instalar Celery

Instalar RabbitMQ

Añadir Celery al proyecto

Añadir tareas asíncronas a su aplicación

Monitorizar Celery

Resumen

8. Gestión de pagos y pedidos

Integrar una pasarela de pago

Crear una cuenta de prueba en Braintree

Instalar el módulo Python de Braintree

Integrar la pasarela de pago

Integrar Braintree con Hosted Fields

Probar los pagos

Desplegando en producción

Exportar órdenes de pedido a CSV

Añadir acciones en el panel de administración

Personalizar vistas en el panel de administración

Generar facturas en PDF dinámicamente

Instalar WeasyPrint

Crear una plantilla PDF

Renderizar ficheros PDF

Enviar ficheros PDF por email

Resumen

9. Extensión de tienda online

Creación de un sistema de cupones

Modelos para cupones

Aplicar un cupón al carro de la compra

Aplicar cupones a órdenes

Añadir internacionalización y localización

Internacionalización con Django

Configuración de internacionalización y localización

Determinar el idioma en Django

Preparar el proyecto para internacionalización

Traducir código Python

Traducción estándar

Traducciones perezosas

Traducciones incluyendo variables

Traducción de plurales

Traducción de código

Traducir plantillas

La etiqueta de plantilla {% trans %}

La etiqueta de plantilla {% blocktrans %}

Traducir plantillas de la tienda

Rosetta: Interfaz de traducción

Traducciones perezosas

Patrones de URL para internacionalización

Añadir un prefijo de idioma al patrón de URL

Traducir patrones de URL

Permitir cambio de idioma a los usuarios

Traducción de modelos con django-parler

Instalar django-parler

Traducir campos de modelo

Integrar traducciones en el panel de administración

Crear migraciones para traducciones de modelos

Adaptar vista para traducciones

Formato localizado

Validar campos de formulario con django-localflavor

Crear un sistema de recomendación

Recomendar productos en base a compras anteriores

Resumen

10. Construir una plataforma e-learning

Configurar el proyecto e-learning

Construir el modelo curso

Registrar los modelos en el sitio de administración

Usar fixtures para crear datos iniciales para los modelos

Utilizar modelos para contenido diverso

Utilizar herencia de modelos

Modelos abstractos

Herencia multitabla de modelos

Modelos Proxy

Crear los modelos de contenido

Campos de modelo personalizados

Añadir orden a objetos de módulos y contenidos

Crear un CMS

Añadir un sistema de autenticación

Crear las plantillas de autenticación

Crear vistas basadas en clases

Utilizar mixins para las vistas basadas en clases

Trabajar con grupos y permisos

Restringir el acceso a las vistas basadas en clases

Gestionar módulos y contenido de los cursos

Utilizar conjuntos de formularios para los módulos del curso

Añadir contenido a los módulos de un curso

Gestionar módulos y contenidos

Reordenar módulos y contenidos

Utilizar mixins de django-braces

Resumen

11. Renderizar y cachear contenido

Mostrar cursos

Añadir el registro de estudiantes

Crear una vista de registro para estudiantes

Inscribir estudiantes en cursos

Acceder al contenido del curso

Renderizar diferentes tipos de contenido

Utilizar el sistema de caché

Sistemas de caché disponibles

Instalar Memcached

Configurar la caché

Añadir Memcached al proyecto

Monitorizar Memcached

Niveles de caché

Usar la API de caché de bajo nivel

Cachear basándose en datos dinámicos

Cachear fragmentos de plantilla

Cachear vistas

Usar caché a nivel de sitio web

Resumen

12. Construir una API

Construir una API RESTful

Instalar Django REST framework

Definir serializadores

Entender los parsers y renderers

Construir vistas de listado y detalle

Crear serializadores anidados

Crear vistas personalizadas

Gestionar la autenticación de usuarios

Añadir permisos a vistas

Crear conjuntos de vistas y enrutadores

Añadir acciones adicionales a conjuntos de vistas

Crear permisos personalizados

Serializar contenidos de cursos

Resumen

13. Lanzamiento en producción

Crear un entorno de producción

Configuración para distintos entornos

Utilizar PostgreSQL

Comprobar su proyecto

Servir Django mediante WSGI

Instalar uWSGI

Configurar uWSGI

Instalar NGINX

El entorno de producción

Configurar NGINX

Servir archivos estáticos y media

Securizar conexiones con SSL

Crear un certificado SSL

Configurar NGINX para utilizar SSL

Configurar su proyecto con SSL

Crear un middleware a medida

Crear un middleware para gestionar subdominios

Servir múltiples dominios con NGINX

Comandos de administración personalizados

Resumen

Prefacio

Django es un potente framework web escrito en Python que fomenta un desarrollo rápido y un diseño limpio y pragmático, a la par que ofrece una curva de aprendizaje relativamente suave, lo que lo hace atractivo tanto para programadores novatos como expertos.

Este libro le guiará a través de todo el proceso del desarrollo de aplicaciones web profesionales con Django. El libro no solo cubre los aspectos más relevantes del framework, sino que también muestra cómo integrar otras tecnologías populares en proyectos Django.

Este volumen le enseñará a crear aplicaciones reales, solucionar problemas comunes e implementar las mejores prácticas mediante un enfoque paso a paso fácil de seguir.

Después de leer este libro sabrá cómo funciona Django y cómo utilizarlo para elaborar aplicaciones web prácticas con funcionalidades avanzadas.

A quién va dirigido este libro

Este libro va dirigido a programadores con conocimientos de Python que quieren aprender Django de una forma práctica. Tanto si Django es un framework nuevo para usted, como si ha trabajado con él pero quiere dominarlo, este libro es el más adecuado para usted. Le ayudará a dominar las funciones más relevantes del framework creando proyectos completos desde cero. Para leerlo es necesario estar familiarizado con conceptos de programación y tener un conocimiento básico de Python, HTML y JavaScript.

Cómo aprovechar este libro

Para sacar el máximo rendimiento a este libro, se recomienda tener un buen conocimiento de Python y conocimientos básicos de HTML y JavaScript. Antes de adentrarse en sus páginas se recomienda leer las partes 1 a 3 del tutorial de la documentación oficial de Django en https://docs.djangoproject.com/es/2.0/intro/tutorial01/.

Descarga del código asociado al libro

En la parte inferior de la primera página del libro encontrará el código de acceso que le permitirá acceder de forma gratuita a los contenidos adicionales en www.marcombo.info.

1
Crear un blog

Con este libro aprenderá a desarrollar proyectos Django, listos para su funcionamiento en producción. En caso de que aún no haya instalado Django, aprenderá cómo hacerlo en la primera parte de este capítulo. En este también se verá cómo crear una sencilla aplicación de blog con Django. El propósito principal de este capítulo es tener una visión general del framework, comprender cómo interactúan los diferentes componentes entre sí y obtener las habilidades necesarias para crear un proyecto Django con funcionalidades sencillas. Este capítulo le guiará en la creación de un proyecto Django completo sin entrar en detalle en cada paso. Los diferentes componentes utilizados en este capítulo se describen en los siguientes capítulos.

Este capítulo trata los siguientes puntos:

• Instalar Django y crear un primer proyecto.

• Diseñar modelos de datos y generar migraciones.

• Crear un sitio de administración para modelos de datos.

• Utilizar QuerySets y Managers.

• Construir vistas, plantillas y URLs.

• Añadir paginación a vistas de listado.

• Usar vistas basadas en clases.

Instalar Django

Si ya ha instalado Django puede saltar a la siguiente sección Crear mi primer proyecto. Django es un paquete Python y, como tal, puede instalarse en cualquier entorno Python. Si aún no ha instalado Django, los siguientes pasos le guiarán para instalarlo en un entorno local de desarrollo.

Django 2.0 requiere la versión de Python 3.4 o superior. En los ejemplos de este libro utilizaremos Python 3.6.5. Si usa un sistema operativo Linux o macOS X, probablemente tenga instalado Python por defecto. Si usa otro tipo de plataformas, como Windows, puede descargar un instalador de Python desde https://www.python.org/downloads/windows/.

En caso de duda, puede comprobar si Python está instalado en su ordenador introduciendo el comando python en una consola. Si tras ejecutarlo aparece un texto similar al mostrado a continuación, Python está instalado en su sistema:

Si la versión de Python instalada es inferior a la 3.4, o si no tiene Python instalado en su sistema, descargue Python 3.6.5 desde https://www.python.org/downloads/ e instálelo. Si dispone de un sistema de gestión de paquetes en su sistema operativo, es recomendable realizar la instalación a través de él.

Al utilizar Python 3 no es necesario instalar una base de datos, ya que esta versión de Python incluye la base de datos SQLite en la instalación por defecto. SQLite es una base de datos ligera que se puede usar con Django para entornos de desarrollo. Para desplegar una aplicación en un entorno de producción, es necesario utilizar otro tipo de bases de datos avanzadas como PostgreSQL, MySQL u Oracle. Puede obtener más información sobre cómo configurar una base de datos con Django en https://docs.djangoproject.com/en/2.0/topics/install/#database-installation.

Crear un entorno de desarrollo Python

Es recomendable utilizar la librería virutalenv para crear un entorno de desarrollo aislado. De esta manera podremos disponer de diferentes versiones de paquetes en los distintos proyectos en los que estemos trabajando. El uso de virutalenv también ofrece independencia de la instalación de Python que haya en nuestro propio sistema. De esta manera, también se cuenta con la ventaja de no necesitar permisos de administración para la instalación de paquetes Python. Para instalar virutalenv ejecute la siguiente instrucción en una línea de comandos:

Tras la instalación de virutalenv cree un entorno de desarrollo independiente con el comando:

Esto generará un directorio my_env/ incluido el nuevo entorno Python. Cualquier librería Python que se instale mientras el entorno virtual permanezca activo se almacenará en el directorio my_env/lib/python3.6/site-packages.

Si su sistema incluía una versión Python 2.X y ha instalado Python 3 es necesario especificar la versión a utilizar durante la creación del entorno virtual con virutalenv.

En caso de querer nuestro entorno virtual con Python 3, es necesario especificar su ubicación durante la creación del mismo. Para ello, se necesita conocer la ruta y crearlo con los siguientes comandos, respectivamente:

Para activar nuestro entorno virtual ejecute el siguiente comando:

Una vez ejecutado, el intérprete de comandos cambiará, incluyendo el nombre del entorno virtual activo entre paréntesis, como se puede apreciar a continuación:

Puede desactivar el entorno virtual en cualquier momento ejecutando el comando deactivate.

Para obtener más información sobre el funcionamiento de virtualenv visite https://virtualenv.pypa.io/en/latest/.

En cuanto a virtualenv puede utilizar la herramienta virtualenvwrapper. Esta herramienta ofrece una abstracción de virtualenv que simplifica la creación y gestión de sus entornos virtuales. Puede descargarlo desde https://virtualenvwrapper.readthedocs.io/en/latest/.

Instalar Django mediante pip

El método más recomendado para instalar Django es haciendo uso del sistema de gestión de paquetes de Python pip. Python 3.6 lleva este paquete preinstalado, pero, en caso necesario, se pueden encontrar instrucciones para su instalación en https://pip.pypa.io/en/stable/installing/.

Ejecute el siguiente comando en intérprete para instalar Django con pip:

Django quedará instalado en el directorio site-packages/ del entorno virtual que esté activo durante la instalación.

Para comprobar que Django se ha instalado correctamente arranque un intérprete de Python. Después importe el paquete recién instalado y compruebe su versión. Para ello es necesario ejecutar:

Si tras la ejecución se obtiene esta salida, Django se ha instalado correctamente.

Django puede instalarse a través de otros métodos. Puede encontrar una guía de instalación más detallada en https://docs.djangoproject.com/en/2.0/topics/install/.

Crear el primer proyecto

El primer proyecto en Django consistirá en construir un blog con toda su funcionalidad. Django ofrece, para su comodidad, un comando que crea toda la infraestructura inicial de ficheros necesaria. Para ello, ejecute el siguiente comando en el intérprete:

Esto generará un directorio de nombre mysite con toda la estructura que necesitará en su interior.

Evite nombres de proyectos que coincidan con los de paquetes que Python tiene por defecto, para que no se produzcan conflictos durante las importaciones.

La estructura de directorios que se acaba de generar es la siguiente:

Estos ficheros son:

• manage.py es un script con diferentes funcionalidades que permiten interactuar con nuestro proyecto. Es un wrapper sobre djangoadmin.py y no es necesario modificarlo.

• mysite/ es el directorio del proyecto, el cual contiene:

○ __init__.py es un fichero vacío que permite indicar a Python que mysite sea utilizado como un módulo de Python.

○ settings.py incluye la configuración y propiedades principales del proyecto.

○ urls.py es un fichero donde se definen los patrones de URL. Cada URL que aparezca apunta a una vista de la aplicación.

○ wsgi.py es la configuración para arrancar nuestro proyecto como una aplicación Web Server Gateway Interface (WSGI).

El fichero generado settings.py contiene la configuración del proyecto, incluida una configuración básica para usar la base de datos SQLite3 y una lista llamada INSTALLED_APPS. Esta lista contiene todas las aplicaciones comunes de Django que se añaden al proyecto por defecto. Más adelante, en la sección de Configuración del Proyecto, revisaremos con más detalle este listado de aplicaciones.

Para terminar la instalación del proyecto, se debe crear, en la base de datos, las tablas que necesitan las aplicaciones de la lista INSTALLED_APPS. Para ello, abra una consola y ejecute ejecutamos los siguientes comandos:

Las últimas líneas de la salida que se obtienen serán:

Las líneas previas son las migraciones de base de datos aplicadas por Django. Al aplicar las migraciones, las tablas de las aplicaciones se crean en base de datos. Descubrirá más sobre el comando de gestión migrate en la sección Crear y aplicar migraciones de este capítulo.

Arrancar el servidor de desarrollo

Django trae incorporado un servidor web ligero para ejecutar el código de manera sencilla y rápida que ahorra el tiempo de configurar un servidor de producción. El servidor de desarrollo de Django revisa los posibles cambios que haya en nuestro código y es capaz de volver a desplegarlos de manera automática tras su detección. Sin embargo, no es sensible a todos los cambios que realicemos, como por ejemplo la incorporación de un nuevo fichero en el proyecto. Por lo que para estos casos se debe de parar el servidor y volverlo a arrancar.

Para arrancar el servidor de desarrollo solo se necesita escribir desde un intérprete el siguiente comando. Es importante que se haga desde la raíz del proyecto:

La salida debería ser similar a:

Acceda a la URL http://127.0.0.1:8000/ desde un navegador. Debería ver una página similar a la siguiente, indicando que el proyecto funciona correctamente:

La imagen anterior refleja que Django está en funcionamiento. Si se mira la consola en la que se ejecuta el servidor, se puede ver una petición GET realizada por el navegador:

Esta petición HTTP es registrada por el servidor de desarrollo en la consola. Cualquier error que se produzca durante la ejecución también se verá aquí.

El servidor de desarrollo de Django admite cierta configuración. Puede indicarle un host y puerto específicos o que cargue una configuración diferente del siguiente modo:

En el caso de que disponga de diferentes entornos en los que desplegar la aplicación, es una buena práctica disponer de un fichero de configuración por entorno.

Es importante recordar que este servidor está solo pensado para su uso durante el desarrollo y que en ningún caso debe utilizarse para un entorno de producción. Para desplegar la aplicación Django en un entorno real se debe de ejecutar como una aplicación WSGI sobre un servidor web como Apache, Gunicorn o uWSGI. Puede obtener más información sobre cómo realizar esta tarea con Django en https://docs.djangoproject.com/en/2.0/howto/deployment/wsgi/.

En el capítulo 13, Lanzamiento en producción, se explica cómo configurar un entorno de producción real para sus proyectos Django.

Configuración del proyecto

Toda la configuración del proyecto se encuentra en el fichero settings.py. Por defecto, durante la creación del mismo, Django incluye en este fichero información relevante que permite arrancar el proyecto, pero en él solo hay una parte de todas las variables que puede llegar a tener. Puede encontrar información sobre todas las variables de configuración y sus posibles valores en https://docs.djangoproject.com/en/2.0/ref/settings/.

A continuación, se repasan las principales variables de configuración:

• DEBUG es una variable booleana que activa o desactiva el modo de depuración en nuestro proyecto. Si está activo (con valor True), Django mostrará una página de error con la traza e información de contexto en caso de excepciones no tratadas. Cuando se despliega sobre un entorno de producción esta variable debe tener valor False. En caso contrario, se expone información sensible del proyecto.

• ALLOWED_HOSTS no se aplica cuando DEBUG está activo o cuando se ejecutan los test. Una vez se mueva el proyecto a un entorno de producción y se desactive la variable DEBUG, se deberá también añadir un dominio o host para poder servir la aplicación.

• INSTALLED_APPS es una variable que será necesario modificar en todos los proyectos y contiene información de las aplicaciones que están activas para cada uno de ellos. Por defecto, Django incluye en esta variable las siguientes aplicaciones:

○ django.contrib.admin: sitio de administración

○ django.contrib.auth: aplicación de autenticación

○ django.contrib.contenttypes: aplicación para la gestión de content types

○ django.contrib.sessions: aplicación de sesiones

○ django.contrib.messages: aplicación de mensajes

○ django.contrib.staticfiles: aplicación para la gestión de contenido estático

• MIDDLEWARE es una lista que contiene las capas intermedias que se ejecutarán.

• ROOT_URLCONF identifica el módulo Python donde están definidos los patrones de URL de la aplicación.

• DATABASES es un diccionario que contiene la configuración de nuestra base de datos. Debe haber siempre una base de datos por defecto. La configuración predefinida hace uso de una base de datos SQLite3.

• LANGUAGE_CODE define el idioma por defecto para nuestro sitio web.

• USE_TZ le indica a Django que tenga en cuenta el huso horario definido en caso de estar activo. Esta variable viene activa cuando se crea el proyecto usando startproject.

Esta es una visión general de algunas de las variables de configuración más importantes. Se detallará su utilidad a lo largo de los capítulos de este libro.

Proyectos y aplicaciones

En este libro podrá encontrar muchas menciones a los términos proyecto y aplicación. En Django, un proyecto se considera una instalación Django incluyendo su configuración. Mientras que una aplicación es un conjunto de modelos, vistas, plantillas y patrones de URL con cierta entidad independiente. Las aplicaciones interactúan con el framework Django para proveer de funcionalidad específica y pueden reutilizarse en diferentes proyectos. Se puede pensar en el proyecto como el sitio web, mientras que una aplicación es un blog, una wiki, un foro o un chat, que conforman el sitio web y pueden reutilizarse a su vez en diferentes sitios web.

Crear una aplicación

Se va a crear la primera aplicación Django y construir un blog desde cero. Para ello se ejecuta, desde la raíz del proyecto, el siguiente comando:

Esto creará la estructura principal de una aplicación, que tiene la siguiente jerarquía de ficheros:

A continuación, se dispone de:

• admin.py, que es el fichero donde se registran los modelos para que sean incluidos en el sitio de administración de Django. Hacer uso del sitio de administración de Django no es obligatorio.

• apps.py, que contiene la configuración principal de la aplicación blog.

• migrations, que es un directorio que contendrá las migraciones de base de datos de la aplicación. Esto permite que Django haga un seguimiento de los cambios en los modelos de datos y que puedan sincronizarse con la base de datos de forma coherente.

• models.py, que contiene los modelos de datos de la aplicación. Toda aplicación de Django requiere de un fichero models.py, pero el fichero puede estar vacío.

• tests.py, que es donde se incluirán los test de la aplicación.

• views.py contiene la lógica de la aplicación. Cada vista recibe una petición HTTP, la procesa y devuelve una respuesta.

Esquema de datos del blog

Se va a comenzar por el diseño del esquema de datos del blog definiendo los modelos. Un modelo es una clase Python que hereda de django.db. models.Model, en la que cada atributo representa un campo de base de datos. Por cada uno de los modelos que se ha definido en el fichero models.py Django creará una tabla en la base de datos. A través de estos modelos, Django ofrece una API que permite realizar acciones sobre objetos de la base de datos de manera sencilla.

Para comenzar, se va a definir un modelo Post. Para ello se debe añadir las siguientes líneas en el fichero models.py de la aplicación blog:

Este es el modelo de datos para los artículos del blog. Los campos que se acaban de definir en el modelo son los siguientes:

• title es un campo que contiene el título del artículo y es de tipo CharField, lo que se traduce en una columna de tipo VARCHAR en la base de datos.

• slug es un campo utilizado para la generación de la URL para el artículo. Es una cadena de texto que puede estar formada por letras, números, guiones medios y/o bajos. Se utilizará este campo para construir URLs amigables y aptas para el SEO del sitio web. Se ha incluido el parámetro unique_for_date a este campo para poder añadir también a la URL la fecha de publicación del artículo. De este modo, Django prevendrá de tener múltiples artículos con el mismo slug para la misma fecha.

• author es una clave foránea que define una relación uno a muchos. De esta forma se indica a Django que un artículo está escrito por un usuario y que, a su vez, un usuario puede ser autor de muchos artículos. Django creará esta relación en la base de datos utilizando la clave primaria del modelo al que se hace referencia, en este caso, el modelo User del sistema de autenticación del framework. El parámetro on_delete especifica el comportamiento que debe tener el artículo cuando el objeto referenciado es eliminado. Esto no es específico de Django sino de la base de datos. Si se usa el valor CASCADE, al eliminar un autor se eliminarán también todos los artículos del blog asociados a este. Para el resto de opciones disponibles puede encontrar más información en https://docs.djangoproject.com/en/2.0/ref/models/fields/#django.db.models.ForeignKey.on_delete. Para especificar el nombre de la relación inversa (de User a Post), se utiliza el parámetro related_name. Esto nos permitirá acceder de manera sencilla a los objetos desde el modelo User.

• body contiene el cuerpo del artículo. Este campo es de tipo texto, el cual se convierte en una columna TEXT en lenguaje SQL de base de datos.

• publish es un campo de tipo fecha y hora que indica el momento de publicación del artículo. Por defecto, se usa el valor now del módulo timezone de Django. Este devuelve el momento actual según el huso horario en que se haya definido nuestro proyecto.

• created, al igual que publish, es un campo de tipo fecha y hora, pero que indica el momento de creación del artículo. Haciendo uso de auto_now_add, el valor se guardará automáticamente en la creación del objeto sin tener que especificarlo de forma explícita.

• updated es también un campo de fecha y hora. Como su nombre indica, muestra el momento del último cambio que sufrió el objeto. Para ello, se emplea auto_now, que actualizará automáticamente este valor.

• status es un campo tipo texto que contendrá los estados en los que puede estar el artículo. Haciendo uso del parámetro choices se limita el valor de status a una lista finita de valores.

Django ofrece una amplia variedad de tipos de campos para construir los modelos. En https://docs.djangoproject.com/en/2.0/ref/models/fields/ se puede encontrar una lista completa de todos los que hay disponibles.

La clase Meta dentro del modelo define metadatos del propio modelo. En este caso se indica a Django que, por defecto, cuando se realice una consulta sobre este modelo en base de datos, ordene los resultados por el campo publish en orden descendente. Esto último, se especifica con el prefijo menos. De este modo, los artículos más recientes aparecerán primero.

El método __str__() es la representación legible del objeto. Django utiliza este método en múltiples lugares, entre otros, el sitio de administración.

Si ha usado Python 2.X, tenga en cuenta que en Python 3 todas las cadenas de texto son consideradas Unicode y, por tanto, solo se utiliza el método __str__(). El método __unicode__() está obsoleto.

Activar la aplicación

Para que Django tenga constancia de la aplicación y poder realizar operaciones en ella, como crear las tablas de los modelos en base de datos, es necesario activarla. Para ello, se necesita abrir el fichero settings.py e incluir blog.apps.BlogConfig en la lista INSTALLED_APPS:

La clase BlogConfig es la aplicación de configuración. Al añadirla al setting INSTALLED_APPS Django tiene constancia de la aplicación y es capaz de cargar los modelos de datos que se defina en la misma.

Crear y aplicar migraciones

Una vez creados los modelos de datos del blog, se necesita una tabla de base de datos para gestionarlos. Django incorpora un sistema de migraciones capaz de gestionar los cambios realizados en los modelos y aplicarlos sobre la base de datos. El comando migrate aplica las migraciones de todas las aplicaciones que se encuentren definidas en la variable INSTALLED_APPS, de modo que sincroniza las tablas de la base de datos con la definición de los modelos.

Lo primero que hay que hacer es crear una migración inicial para el modelo Post. Para ello, se debe ejecutar el siguiente comando:

Para el que se debería obtener la siguiente salida:

Django acaba de crear el fichero 0001_initial.py dentro del directorio migrations de la aplicación blog. Si se mira el contenido del fichero, se puede ver cómo una migración define dependencias entre migraciones y operaciones para realizar en la base de datos. Esto permite la sincronización que se mencionó antes entre la base de datos y el modelo.

A continuación, se describe el código SQL que Django ejecutará en la base de datos para crear la tabla del modelo. El comando sqlmigrate recibe el nombre de la migración y devuelve las sentencias SQL correspondientes sin llegar a ejecutarse. Utilice el siguiente comando para revisar la salida SQL de la migración anterior:

La salida debe tener el siguiente aspecto:

La salida dependerá del tipo de base de datos que se utilice. Esta ha sido generada para SQLite. Tal como se puede apreciar en el código SQL, Django genera un nombre de tabla combinando el nombre de la aplicación junto con el nombre del modelo (blog_post). Este comportamiento se puede modificar con el atributo db_table en la clase Meta del modelo. Django también crea por defecto una clave primaria por cada modelo. El campo que utiliza como clave primaria es la columna id de tipo entero con incremento automático. Django también permite modificar la clave primaria utilizando el parámetro primary_key=True en uno de los campos del modelo.

Se va a sincronizar la base de datos con el modelo. Para ello hay que aplicar las migraciones ejecutando el siguiente comando:

Se obtiene una respuesta de varias líneas, correspondiendo la última con:

Con esto se aplican las migraciones de todas las aplicaciones declaradas en INSTALLED_APPS, incluida la aplicación blog. Tras aplicar las migraciones, la base de datos refleja el estado actual de los modelos.

Si se edita el fichero models.py, añadiendo, eliminando o modificando alguno de los campos existentes, o si se añaden nuevos modelos, se deberá crear una nueva migración a través del comando makemigrations. La migración permite a Django estar al tanto de los cambios realizados en los modelos. Una vez generada la migración, se aplica para sincronizar la base de datos mediante el comando migrate.

Sitio de administración para los modelos

Tras haber definido el modelo Post, se va a crear un sitio web de administración para gestionar los artículos del blog. Django incluye un sistema web de administración muy completo para la edición de contenido. Este sistema se construye dinámicamente a través de la metainformación de los modelos y es muy sencillo de configurar.

La aplicación django.contrib.admin correspondiente al sitio de administración, está incluida en la variable de configuración INSTALLED_APPS, por lo que no es necesario añadirla de nuevo.

Crear un superusuario

Lo primero que se necesita es crear un usuario que pueda gestionar el sitio web de administración. Para ello se ejecuta el siguiente comando:

Se obtiene la siguiente salida. Introduce un usuario, un email y un password como se ve a continuación:

El sitio de administración de Django

Inicie el servidor de desarrollo mediante el comando python manage.py runserver y utilice un navegador para acceder a http://127.0.0.1:8000/admin/. Debería ver la siguiente página de autenticación del sitio de administración:

Acceda con las credenciales de usuario creadas anteriormente. Una vez dentro, se ve el panel inicial del sitio de administración:

Los modelos Group y User que se pueden ver en la imagen pertenecen a la aplicación de autenticación de Django correspondiente al paquete django.contrib.auth. Si se pulsa sobre el enlace Users se ve la información del usuario creado anteriormente. El modelo Post de la aplicación blog tiene una relación con este modelo User. Existe una relación declarada en el campo author.

Añadir modelos al sitio de administración

Se van a añadir los modelos del blog en el sitio de administración de Django. Para ello es necesario editar el fichero admin.py de la aplicación blog y añadir el siguiente código:

Para ver los cambios solo hay que recargar la página en el navegador. Ahora aparece el modelo Post en el sitio de administración:

De este modo, se consigue una interfaz amigable y sencilla para el listado, edición, creación y eliminación de objetos para modelos de datos.

Pulse sobre el enlace de Add a la derecha de Posts para crear un nuevo artículo. Esto conduce a un formulario generado dinámicamente por Django para el modelo como se puede ver a continuación:

Django utiliza diferentes widgets en los formularios según el tipo de campo a representar, incluyendo elaborados selectores para campos como el de tipo DateTimeField, que hace uso de selectores en JavaScript.

Tras rellenar el formulario haga clic sobre el botón Save. Si se ha hecho correctamente, le redirigirá a la página de lista de artículos incluyendo un mensaje indicando que el nuevo artículo ha sido creado correctamente:

Personalizar la vista de modelos

En muchas ocasiones interesará personalizar la forma en la que se muestra la información almacenada en el modelo. A continuación se describe cómo personalizar el sitio de administración.

Edite el fichero admin.py de la aplicación blog para que quede de la siguiente forma:

Con este código se indica al sitio de administración de Django que se registra un modelo con una clase propia que hereda de ModelAdmin. De este modo se puede incluir información sobre cómo visualizar la información del modelo y cómo interactuar con él. El atributo list_display permite definir los campos del modelo que aparecerán en el listado de objetos del sitio de administración y su orden. El decorador @admin.register() desempeña la misma tarea que la función admin.site.register(), registrando la clase ModelAdmin que decora.

Se personaliza el modelo de administración con el siguiente código:

Tras añadirlo, se vuelve al navegador y se refresca la página. Ahora debería ver esto:

Como se aprecia, los campos que aparecen en el listado de artículos son los que se especifica en el atributo list_display. Este listado ahora también incluye una barra en el lado derecho para filtrar resultados por los campos especificados en list_filter. Ha aparecido una caja de búsqueda debido a que se ha incluido el atributo search_fields. Con él se puede realizar búsquedas sobre los campos definidos. También han aparecido enlaces bajo la caja de búsqueda que permiten navegar a través de una jerarquía de fechas. Se ha definido en el atributo date_hierarchy. El listado de artículos tiene una ordenación por defecto que viene definida en el atributo ordering. En este caso, por estado y fecha de publicación.

Ahora se va a pinchar sobre el enlace Add Post. Aquí también se aprecian algunas diferencias. Mientras escribe el título del nuevo artículo, el campo slug se va completando de forma automática. Con el código anterior, se ha indicado a Django, a través del atributo prepopulated_fields, que complete este campo a partir del título del artículo. El campo author también cambia, añadiendo un widget de búsqueda en lugar de un desplegable. Esto resulta especialmente útil cuando la base de datos crece y existen miles de usuarios en el sistema que se pueden seleccionar.

Con unas pocas líneas de código se ha personalizado el modo en el que interactuar con el modelo en el sitio de administración. Existen muchos modos de personalizar y extender modelos. Se describen otras formas más adelante.

Trabajando con QuerySets y Managers

Se ha creado un sitio de administración funcional para gestionar el contenido del blog. Ahora se va a aprender cómo recuperar información desde la base de datos e interactuar con ella. Django ofrece una potente API de abstracción de la base de datos que permite crear, recuperar, actualizar y eliminar objetos con facilidad. El mapeador objeto-relacional u Object-Relational Mapper (ORM) es compatible con MySQL, PostgreSQL, SQLite y Oracle. Recuerde que puede definir la base de datos del proyecto en la variable de configuración DATABASES del fichero settings.py. Django es capaz de trabajar con múltiples bases de datos en el mismo proyecto, pudiendo operar con distintos esquemas de enrutado de datos.

Una vez se han creado los modelos de datos, Django ofrece el ORM para interactuar con ellos. Puede ver la referencia de modelos de datos de la documentación oficial en https://docs.djangoproject.com/en/2.0/ref/models/.

Crear objetos

Abra un terminal y ejecute el siguiente comando para abrir el intérprete de Python:

Una vez hecho esto ejecute las siguientes sentencias:

A continuación, se analiza la función de este código. Lo primero que hay que hacer, tras las importaciones, es recuperar el objeto user con el nombre de usuario admin:

El método get devuelve un único elemento de la base de datos. Es importante que la consulta devuelva un único resultado. En caso de no haber resultados, se generará una excepción de tipo DoesNotExist. Si, por el contrario, la consulta devuelve más de un resultado, la excepción generada será MultipleObjectsReturned. Ambas excepciones son atributos de la clase Model sobre la que se procesa la consulta.

Después se crea una instancia de tipo Post con un título, slug y contenido específicos, y se enlaza por autor el usuario anteriormente recuperado.

En este momento, este objeto persiste en la memoria no en la base de datos.

Por último, se guarda el objeto Post en la base de datos utilizando el método save:

Esta sentencia realiza una acción INSERT SQL por debajo. Se ha visto cómo crear un objeto en memoria para después almacenarlo en la base de datos, pero también se puede usar el método create, capaz hacer ambas cosas como una única operación:

Actualizar objetos

Para modificar un objeto, por ejemplo, el título del artículo, solo hay que modificar el atributo y salvar el objeto:

En este caso, el método save() realiza una acción UPDATE de SQL.

Los cambios que sufre un objeto no persisten en la base de datos hasta que no se invoca el método save().

Recuperar objetos

El Object-Relational Mapper (ORM) se basa en QuerySets. Un QuerySet es una colección de objetos de la base de datos que puede tener múltiples filtros para reducir el número de resultados. Ya sabe cómo recuperar un único objeto de la base de datos a través del método get(). Se ha hecho uso de él en Post.objects.get(). Cada modelo de Django tiene al menos un manager que, por defecto, se llama objects. Se puede obtener un objeto QuerySet a través del gestor de modelos. Si quisiera, por ejemplo, obtener todos los objetos de una tabla, solo hay que usar el método all() del gestor por defecto:

Este es el modo en que crear un objeto QuerySet que devuelva todos los objetos en una base de datos. Es importante mencionar, que este QuerySet no se ha ejecutado aún en la base de datos. Los QuerySets en Django tienen un comportamiento perezoso (lazy), es decir, solo se evalúan cuando son forzados a ello, dando como resultado un comportamiento muy eficiente. Si en vez de asignar el QuerySet a una variable, lo escribe en la shell de Python, la sentencia SQL del QuerySet se ejecuta, porque se ha forzado a que los resultados aparezcan por pantalla:

Valiéndose del método filter()

Para filtrar un QuerySet se puede valer del método filter() del gestor. Por ejemplo, para recuperar todos los artículos publicados en el año 2017, utilizaría el siguiente filtrado:

También se puede concatenar múltiples campos de filtrado. Por ejemplo, recuperando los artículos publicados en 2017 y cuyo usuario del autor sea admin:

Esto es lo mismo que construir el mismo QuerySet con múltiples filtros:

Para realizar consultas sobre métodos de búsqueda de campos, se utilizan dos guiones bajos, por ejemplo, publish__year. Sin embargo, la misma notación se utiliza para acceder a campos de modelos relacionados, como author__username.

Uso de exclude()

Se pueden excluir resultados del QuerySet a través del método exclude() del gestor. Por ejemplo, se pueden recuperar todos los posts publicados en 2017 cuyos títulos no empiezan por “Why”:

Uso de order_by()

Los resultados se pueden ordenar con los campos a través del método order_by() del gestor. Por ejemplo, si quisiera obtener los artículos ordenados por el campo title, los obtendría con:

El comportamiento por defecto es en orden ascendente. Si lo quisiera descendente, utilizaría el prefijo negativo del siguiente modo:

Eliminar objetos

Si quiere eliminar un objeto puede hacerlo con el método delete() del mismo:

Eliminar objetos provocará también la eliminación de objetos cuyas relaciones a través de ForeignKey estén definidas con el campo on_delete con valor CASCADE.

Cuándo se evalúan los QuerySets

Ya ha visto que se pueden concatenar tantos filtros a un QuerySet como quiera, y que la base de datos no procesará la acción hasta que el QuerySet sea evaluado. Estos se evalúan en las siguientes situaciones:

• La primera vez que se itere sobre ellos.

• Cuando accedemos a un elemento/s por posición, por ejemplo, Post.objects.all()[:3].

• Cuando los seleccionamos o guardamos en caché.

• Cuando los invocamos con repr() o len().

• Cuando instanciamos una lista sobre ellos.

• Cuando realizamos una operación lógica como bool(), or, and, o if con ellos.

Crear gestores de modelos

Ya mencionamos que objects es el gestor por defecto de cualquier modelo para recuperar objetos de la base de datos. Sin embargo, también se pueden definir gestores personalizados para los modelos. Se va a crear un gestor que recupere todos los artículos con el estado published.

Existen dos maneras de añadir gestores a los modelos: añadiendo métodos a los gestores ya existentes o modificando el gestor por defecto de QuerySets. La primera proporciona un método al API del QuerySet como Post.objects. my_manager() y, la segunda, como Post.my_manager.all(). En este caso, el gestor permitirá recuperar los artículos usando Post.published.all().

Para ello editamos el fichero models.py de la aplicación blog para incluir el nuevo gestor:

El método get_queryset() de un gestor devuelve el QuerySet ejecutado. Modificamos este método para incluir el filtrado al final del QuerySet. Se ha definido el gestor personalizado y añadido al modelo Post. Con esto ya puede utilizarlo para realizar consultas.

Se va a arrancar una consola de desarrollo con el siguiente comando:

Ahora, para recuperar todos los artículos publicados cuyo título empieza por Who, se utiliza la siguiente sentencia:

Elaborar vistas de detalle y listado

Se ha visto cómo se usa el ORM de Django. A continuación, se elaboran vistas en la aplicación del blog. Una vista de Django es una función Python que recibe una solicitud web y devuelve una respuesta web. Toda la lógica de procesamiento se encuentra dentro de la vista.

Lo primero que se va a hacer es crear las vistas de la aplicación, después se definen los patrones de URL para cada una de ellas y, por último, se crean las plantillas de HTML para renderizar el contenido con los datos de la vista. Cada vista renderiza una plantilla que se completa con datos de variables. Esta renderización se devuelve en una respuesta HTTP.

Creación de vistas de detalle y listado

Se empieza con la elaboración de una vista que muestre el listado de artículos disponibles. Para ello se edita el fichero views.py de la aplicación blog con el siguiente código:

Acaba de crear su primera vista de Django, post_list, que recupera un objeto request como único parámetro. Este parámetro es obligatorio para todas las vistas y se recibirá siempre como primer argumento. En esta vista se recuperan todos los artículos con el estado published utilizando el gestor published creado anteriormente.

Por último, se hace uso del atajo render() que ofrece Django. Con él se renderizar la lista de artículos con la plantilla proporcionada como parámetro. Esta función recoge el objeto request, la ruta relativa de la plantilla y el contexto de variables para su correcta renderización, devolviendo un objeto HttpResponse con el texto renderizado (habitualmente HTML). Es necesario pasar el objeto request para que cualquiera de las variables definidas por los procesadores de contexto también sea accesible por la plantilla. Los procesadores de contexto de las plantillas son funciones que inicializan variables en el contexto. Este tema se describe detalladamente en el capítulo 3, Extensiones para el blog.

El paso siguiente es crear una segunda vista que muestre información de un artículo. Para ello hay que añadir el siguiente código al fichero views.py:

Esta es la vista detallada de un artículo. Esta vista toma los parámetros year, month, day y post para recuperar un artículo publicado con dicho slug y fecha. Hay que mencionar que durante la creación del modelo Post, se incluyó el parámetro unique_for_date en el campo slug. De este modo asegurará la singularidad de un slug para un día dado y, por tanto, la posibilidad de recuperar un único elemento dado un slug y una fecha. En la vista detallada se usa el atajo get_object_or_404() de Django para recuperar el artículo. Esta función devuelve un objeto que coincide con los datos proporcionados o lanza una excepción HTTP 404 (no encontrado) en caso de no hallarlo. Por último, se llama a la función render() para renderizar la información del artículo a través de una plantilla.

Añadir patrones de URL para las vistas

Los patrones de URL permiten relacionar URLs con vistas. Un patrón de URL está compuesto por un patrón de texto, una vista y, opcionalmente, un nombre que permite referenciar con mayor facilidad el patrón que va a crear. Django recorre cada uno de estos patrones de URL hasta encontrar el primero que coincida con la URL solicitada. Entonces, Django importa la vista relacionada y la ejecuta, pasándole el objeto de tipo HttpRequest y el resto de parámetros, si los hubiese.

Se va a crear el fichero urls.py en el directorio de la aplicación blog y añadirá las siguientes líneas:

En el código anterior se ha definido un espacio de nombres de aplicación con la variable app_name. Esto permite organizar las URLs por aplicación y usar dicho nombre cuando se refiera a ellas. En este caso, se definen dos patrones usando la función path(). El primer patrón de URL no toma ningún argumento y está relacionado con la vista post_list. El segundo toma cuatro argumentos y ejecuta la vista post_detail:

• year: requiere un entero.

• month: requiere un entero.

• day: requiere un entero.

• post: composición de letras y guiones.

Los elementos encerrados entre símbolos menor-que y mayor-que recuperan valores de las URLs. Cualquier valor especificado en el patrón como <parameter> es recogido como cadena de texto. Se usan conversores para <int:year> para hacer coincidir con la subcadena de la URL y devolver un número entero. Por otro lado, <slug:post> trata de realizar la misma acción, solo que en este caso tratará de hacer coincidir con un slug (una cadena de texto de caracteres ASCII de número y/o letras, pudiendo contener también guiones medios o bajos). Se puede encontrar más información sobre los conversores de Django en https://docs.djangoproject.com/en/2.0/topics/http/urls/#path-converters.

Si la función path() y los conversores no fueran suficientes, se puede hacer uso de re_path() para definir patrones de URL complejos con expresiones regulares Python. Puede obtener más información sobre este método en https://docs.djangoproject.com/en/2.0/ref/urls/#django.urls.re_path. En caso de no haber trabajado antes con expresiones regulares, es conveniente revisar la sección Regular Expression HOWTO en https://docs.python.org/3/howto/regex.html.

La creación de un fichero urls.py por cada aplicación es la mejor manera de mantener aplicaciones reutilizables por otros proyectos.

Ahora hay que incluir patrones que acaba de crear en los patrones raíz del proyecto. Para ello, editará el fichero urls.py localizado en el directorio mysite del proyecto del siguiente modo:

El nuevo patrón de URL definido con un include se refiere a los patrones de URL que se crean en la aplicación blog, de modo que quedan bajo la ruta blog/. También se incluyen bajo el espacio de nombres blog. Esto permite hacer referencia a URL de un modo más sencillo, como, por ejemplo, blog:post_list o blog:post_detail. Más adelante se describe la utilidad que tienen, pero por ahora, basta con que sepa que consisten en reflejar que los espacios de nombres son únicos en todo el proyecto. Puede obtener más información sobre los espacios de nombres en https://docs.djangoproject.com/en/2.0/topics/http/urls/#url-namespaces.

Crear plantillas para vistas

Una vez tiene las vistas y los patrones de URL de la aplicación blog, es momento de añadir las plantillas para mostrar artículos con cierta armonía.

Para ello, se va a crear la siguiente estructura de directorios y ficheros dentro de la aplicación blog:

Esta estructura contendrá las plantillas para las vistas. El fichero base.html incluirá el contenido principal de HTML, que estará dividido por el área de contenido, el menú y una barra lateral. Este contenido será reutilizado por las diferentes plantillas de forma que su edición sea más sencilla y los cambios se propaguen a lo largo de todas las vistas. Los ficheros list.html y detal.html heredarán del fichero base.html para pintar el listado artículos y la vista de detalle, respectivamente.

Django posee un lenguaje de plantillas muy potente que permite especificar los datos a mostrar. Está basado en etiquetas (template tag), variables (template variable) y filtros (template filter) de plantilla:

• Las etiquetas controlan la renderización de la plantilla y son semejantes a {% tag %}.

• Las variables son reemplazadas con valores cuando la plantilla se renderiza y se representan como {{ variable }}.

• Los filtros permiten modificar variables para ser renderizadas de forma personalizada y son {{ variable|filter }}.

Para obtener más detalles sobre los filtros y etiquetas de que dispone Django se puede visitar https://docs.djangoproject.com/en/2.0/ref/templates/builtins/.

Edite el fichero base.html para añadir el siguiente código:

{% load static %} le indica a Django que cargue la plantilla static, contenida en la aplicación django.contrib.staticfiles, el cual está definido en la variable de configuración INSTALLED_APPS. Tras cargarla, se puede utilizar el filtro {% static %} en la plantilla, el cual permite cargar contenido estático, como el fichero blog.css, alojado bajo el directorio static/ de la aplicación blog. Copie el directorio static/ del código disponible para este capítulo en la misma localización del proyecto para aplicar las hojas de estilo CSS.

También se pueden ver dos etiquetas {% block %}. Estas definen una región a Django. Las plantillas que heredan de esta plantilla base pueden incluir contenido dentro de estos bloques. Se han definido dos bloques, uno llamado title y otro content.

Edite el fichero post/list.html con el siguiente código:

Con la etiqueta {% extends %} se indica a Django que herede de la plantilla blog/base.html. De este modo, se puede redefinir el contenido de los bloques title y content de la plantilla base con otro contenido. Para esto se iteran sobre los artículos y se muestra: su título, fecha de publicación, autor y contenido, incluyendo el enlace canónico al artículo en el título. En el contenido se aplican dos filtros: truncatewords, que trunca el valor a un número específico de caracteres, y linebreaks, que convierte la salida en saltos de línea de HTML. Se pueden concatenar tantos filtros como quiera, donde cada uno será aplicado con la salida generada por el filtro anterior.

Abra el intérprete y ejecute el comando python manage.py runserver para arrancar el servidor de desarrollo. Abra en el navegador http://127.0.0.1:8000/blog/ y verá corriendo la aplicación. Recuerde que necesita algún artículo en el estado published para que pueda verlos aquí. El aspecto debería ser:

Ahora se va a editar el fichero post/detail.html:

Tras editarlo, se puede volver al navegador y pulsar sobre el enlace del título para acceder a la vista de detalle. Debería aparecer:

Si mira a la URL, verá /blog/2017/12/14/who-was-django-reinhardt/. Se han diseñado URLs para el artículo aptas para SEO.

Añadir paginación

En el momento en el que empiece a añadir contenido a el blog, pronto se dará cuenta de que es necesario dividir el listado de artículos en diferentes páginas. Django trae ya una clase capaz de realiza esta paginación. Modifique la vista post_list del fichero views.py de la aplicación blog del siguiente modo:

Así es como funciona la paginación:

1. Se instancia un objeto de la clase Paginator con el número de objetos que queremos visualizar en cada página.

2. Se obtiene el parámetro GET page que indica la página actual que se va a mostrar.

3. Se obtienen los objetos adecuados a mostrar según la página llamando al método page() de Paginator.

4. Si el parámetro page no es un entero, obtiene la primera página del listado. Si el número es mayor que la última página de resultados, obtendrá la última página.

5. Pasa el número de página y el subconjunto de artículos a la plantilla.

Ahora hay que crear una plantilla que muestre el paginador, de modo que pueda ser incluido en cualquier plantilla que lo necesite. Se va a crear una nueva plantilla dentro del directorio templates/ de la aplicación blog, añadiendo el siguiente contenido:

La plantilla del paginador espera un objeto Page para poder visualizar información de la página actual, el número total de páginas y los enlaces de página siguiente y anterior. Se vuelve a la plantilla blog/post/list.html para incluir la plantilla pagination.html al final del bloque {% content %}, del siguiente modo:

Dado que la plantilla de paginación hace uso de la variable page para paginar, así relaciona dicha variable con la que contiene los artículos en el listado de artículos. De este modo se puede reutilizar la plantilla de paginación para cualquier otro modelo.

Si va al navegador y visita http://127.0.0.1:8000/blog/, debería ver la paginación al final del listado de artículos y los enlaces de navegación.

Usar vistas basadas en clase

Las vistas de tipo basadas en clase son una alternativa para implementar vistas como objetos Python en vez de funciones. Mientras que una vista es un elemento al que se puede llamar y que toma una solicitud web y devuelve una respuesta, también se pueden definir vistas como métodos de clase. Django aporta clases base para poder hacerlo de forma sencilla. Todas ellas heredan de la clase View, la cual interactúa con el envío y recepción de contenido HTTP y otras funcionalidades comunes.

Las vistas basadas en clase ofrecen algunas ventajas frente a las vistas de función:

• Permiten organizar el código según métodos HTTP, como GET, POST o PUT, en métodos de clase separados, en vez de usar sentencias condicionales.

• Permiten utilizar herencia múltiple para crear vistas reutilizables (también conocidas como mixins).

Para obtener más información, la documentación de Django ofrece una introducción a las vistas basadas en clase en https://docs.djangoproject.com/en/2.0/topics/class-based-views/intro/.

Se va a cambiar la vista post_list para que funcione como una vista basada en clase usando la clase ListView de Django. Esta clase permite listas de objetos de cualquier tipo.

Edite el fichero views.py de la aplicación blog y añada las siguientes líneas:

Esta vista basada en clase es análoga a la vista previa post_list. En el código anterior ha especificado a ListView lo siguiente:

• Utiliza un QuerySet específico en vez de utilizar el que devuelve todos los objetos. En vez de definir el atributo queryset, podría haber especificado model = Post y Django hubiera construido el QuerySet genérico Post.objects.all() por usted.

• El nombre de la variable de contexto que contiene todos los elementos es posts. Si no se especifica nada, el nombre por defecto es object_list.

• Que cada página contenga tres elementos.

• La plantilla que debe utilizar para renderizar la página, sino ListView utilizará la plantilla por defecto blog/post_list.html.

Queda por modificar el fichero urls.py de la aplicación blog. Comente el patrón de post_list y añada un nuevo patrón de URL para la clase PostListView del siguiente modo:

Para poder mantener la paginación en funcionamiento, es necesario pasar a la plantilla el objeto de página correcto. La vista genérica ListView de Django pasa la página seleccionada en una variable llamada page_obj, por lo que hay que editar la plantilla post/list.html de acuerdo con dicho nombre. Para ello realizará la siguiente modificación:

Para verificar que todo funciona correctamente del mismo modo que lo hacía la vista post_list, abra http://127.0.0.1:8000/blog/ en el navegador. Este es un ejemplo sencillo de una vista basada en clase que utiliza una clase genérica provista por Django. Se profundizará más sobre vistas basadas en clase en el capítulo 10, Construir una plataforma E-Learning, y en capítulos sucesivos.

Resumen

En este capítulo se han descrito los elementos básicos de Django para la creación de una aplicación tipo blog. Se han diseñado los modelos de datos, aplicando migraciones al proyecto. También se han creado las vistas, plantillas y patrones de URL necesarios para el blog, incluyendo la paginación.

En el siguiente capítulo se describe cómo mejorar el blog con un sistema de comentarios y etiquetado y permitir a los usuarios compartir artículos por email.

2
Funciones avanzadas del blog

En el capítulo anterior creó una aplicación sencilla tipo blog. En este capítulo se mejorará la aplicación con funcionalidades avanzadas como compartir artículos por email, añadir comentarios, etiquetar artículos y recuperar artículos por semejanza. En este capítulo se tratarán los siguientes puntos:

• Envío de emails con Django.

• Creación y gestión de formularios en vistas.

• Creación de formularios a partir de modelos de datos.

• Integración con aplicaciones de terceros.

• Construcción de QuerySets complejos.

Compartir artículos por email

Lo primero que se hará será permitir que los usuarios compartan artículos enviando un correo electrónico. Piense cómo podría usar las vistas, los patrones de URL y las plantillas para crear esta funcionalidad con lo que aprendió en el capítulo anterior. Ahora, se detallará qué necesita para permitir a los usuarios enviar artículos por email:

• Crear un formulario para los usuarios para rellenar su nombre y correo electrónico, el correo del destinatario y un comentario, como campo opcional.

• Crear una vista en el fichero views.py que gestione los datos y envíe el email.

• Añadir un patrón de URL para la nueva vista en el fichero urls.py de la aplicación blog.

• Crear una plantilla para visualizar el formulario.

Crear formularios en Django

A continuación, se describe cómo elaborar un formulario para compartir artículos. Django tiene un sistema de formularios con el que crear formularios web de una manera sencilla. Este sistema permite definir los campos del formulario, el modo en que se visualizan y la forma de validación de la entrada de datos. Con este sistema, Django ofrece una forma sencilla y flexible de visualizar formularios y gestionar datos.

Django incluye dos clases base para la elaboración de formularios:

• Form, que permite construir un formulario estándar.

• ModelForm, que permite construir un formulario a partir de una instancia de modelo de datos.

Primero cree el fichero forms.py dentro del directorio blog de la aplicación con el siguiente contenido:

Este es el primer formulario de Django. La clase que ha creado hereda de la clase base Form, donde se definen diferentes atributos con tipos específicos, encargados de la validación de los campos.

Los formularios pueden declararse en cualquier lugar de un proyecto Django. Por convención, el lugar definido para ellos es el fichero forms.py de cada aplicación.

El tipo del campo name es CharField. Este tipo de campo se renderiza como <input type=”text”> en código HTML. Cada tipo de campo tiene una representación HTML (o widget) asociada, que puede ser redefinida a través del parámetro widget. En el caso del campo comments, el widget definido es de tipo Textarea que quedará renderizado como <textarea> en HTML en lugar de <input>.

La validación de campos depende del tipo de campo definido. Por ejemplo, los campos email y to son campos del tipo EmailField. Ambos campos requieren una dirección válida de correo electrónico; en caso contrario, la validación de campos provocará una excepción de tipo forms.ValidationError y el formulario no podrá ser validado. Otros parámetros se tienen en cuenta a la hora de realizar la validación: hay un número máximo de caracteres definidos en el campo name, y también se especifica que el campo comments no es obligatorio. Todos estos parámetros se tienen en cuenta a la hora de realizar las validaciones. El tipo de campos que ha usado son solo una pequeña muestra de los que Django dispone. Puede encontrar información sobre todos los campos de formulario disponibles en https://docs.djangoproject.com/en/2.0/ref/forms/fields/.

Usar formularios en vistas

Ahora va a crear una nueva vista que gestione el formulario y el envío de email, siempre y cuando contenga información válida. Para ello editará el fichero views.py de la aplicación blog y añadirá el siguiente contenido:

Esta vista funciona del siguiente modo:

• Se define una vista llamada post_share que recibe un objeto request y un parámetro post_id.

• Haciendo uso del atajo get_object_or_404() se recupera el artículo por identificador y por estado published.

• Usará la misma vista para visualizar el formulario inicial y procesar la información enviada. Se identifica si el formulario ha sido enviado mediante el atributo method del objeto request. El formulario se envía por el método POST. Si se recibe una solicitud GET, se genera un formulario vacío. Si se recibe una petición POST, se debe procesar la información del formulario. Por este motivo, utilizará la sentencia condicional request.method == 'POST' para distinguir entre ambos escenarios.

Los siguientes pasos corresponden al procesamiento del formulario:

1. Cuando la vista se carga inicialmente con una solicitud GET, se crea una nueva instancia de formulario que se mostrará vacía en la plantilla HTML.

2. El usuario rellena el formulario y lo envía por método POST. En ese momento, se crea una instancia de formulario con los datos enviados contenidos en request.POST:

3. Después de esto se validan los datos enviados usando el método is_valid() del formulario. Este método validará la información introducida en el formulario y devolverá True si todos los campos contienen datos válidos. Si al menos uno de los campos no hubiera podido validarse, el método devolverá False. A partir de entonces, la lista de errores de validación estará accesible en form.errors.

4. Si el formulario no es válido, se renderizará la plantilla de nuevo, con la información enviada, mostrando un mensaje de error en las validaciones que hubiesen fallado.

5. Si el formulario es válido, se recuperarán los datos validados a través de form.claened_data. Este atributo es de tipo diccionario, donde las claves son los nombres de los campos del formulario.

Si el formulario no es válido, cleaned_data solo contendrá los campos válidos.

A continuación se describe cómo se envían correos electrónicos con Django para completar la funcionalidad.

Enviar emails con Django

Enviar emails con Django es una acción muy directa. Primero hay que disponer de un servidor SMTP local o definir uno externo en la configuración, incluyendo la siguiente información en el fichero de configuración settigns.py del proyecto:

• EMAIL_HOST es el nombre donde se aloja el servidor SMTP. Por defecto es localhost.

• EMAIL_PORT es el puerto del SMTP. Por defecto es 25.

• EMAIL_HOST_USER es el usuario del servidor SMTP.

• EMAIL_HOST_PASSWORD es la contraseña de usuario del servidor SMTP.

• EMAIL_USE_TLS indica si debe usar la conexión segura sobre TLS.

• EMAIL_USE_SSL indica si usar una conexión TLS implícita.

Si no dispone de un servidor SMTP, puede indicar a Django que escriba los emails en la consola, a través de la siguiente línea en la configuración:

Con esta configuración, Django escribirá todos los emails a través del intérprete. Esta opción es útil para tareas de validación de la aplicación sin disponer de un servidor SMTP.

Si lo que quiere es enviar emails, pero no dispone de un servidor SMTP local, puede usar la configuración del servidor SMTP del proveedor de servicio de email. Por ejemplo, la siguiente configuración es válida para enviar correos electrónicos a través de los servidores de Gmail con una cuenta de Google:

Ejecute el comando python manage.py shell para abrir un intérprete de Python y enviar un email del siguiente modo:

La función send_mail() recoge el asunto, el mensaje, el remitente y una lista de los destinatarios como argumentos necesarios. Entre los argumentos opcionales existe fail_silently=False, que permite no elevar excepciones en caso de no poder mandar correctamente el email. Como salida se obtiene el número de emails enviados correctamente, por lo que si la salida obtenida es 1, entonces el email ha sido enviado.

Si envía emails con Gmail con la configuración anterior, hay que tener en cuenta activar el acceso para aplicaciones menos seguras en https://myaccount.google.com/lesssecureapps del siguiente modo:

Ahora, va a incorporar la funcionalidad dentro de la vista.

Para ello va a editar la vista post_share en el fichero views.py de la aplicación de blog:

Ha declarado una variable sent y la ha inicializado a True cuando el artículo se envía. Esta variable la usará más tarde para mostrar un mensaje en la plantilla, indicando que el formulario se ha enviado correctamente. Dada la necesidad de incluir en el correo un enlace al artículo, es necesario recuperar la ruta absoluta con el método get_absolute_url(). Se usa esta ruta como parámetro de entrada para request.build_absolute_uri(), construyendo una URL completa, incluyendo el esquema HTTP y nombre de dominio. Para construir el asunto y el cuerpo del mensaje se utilizan los datos procesados del formulario, una vez validado, y, por último, se envía el correo a la dirección estipulada en el campo to del formulario.

Ahora que la vista está completa, recuerde que es necesario añadir un nuevo patrón de URL. Para ello habrá que editar el fichero urls.py de la aplicación blog e incluir el patrón para la vista post_share:

Renderizar formularios en plantillas

Después de haber creado el formulario, programado la vista y añadido el patrón de URL, solo falta la plantilla de la vista. Para ello va a crear un nuevo fichero dentro de la ruta blog/templates/blog/post/ llamado share.html con el siguiente contenido:

Esta es la plantilla encargada de mostrar el formulario o un mensaje en caso de haber podido mandar el email. Como se puede observar, ha creado el elemento de formulario para que sea enviado por el método POST:

Después, le sigue la instancia del formulario. Le indicará a Django que renderice los campos en HTML con modo párrafo, es decir, con elementos <p> a través del método as_p. También puede mostrar el formulario como una lista de elementos utilizando as_ul, o como una tabla, a través del método as_table. Si quiere renderizar cada campo, puede iterar sobre cada uno de ellos del siguiente modo:

La etiqueta de plantilla {% csrf_token %} introduce un campo oculto con un token autogenerado para evitar ataques de tipo Cross-Site Request Forgery (CSRF). Este ataque consiste en un programa o sitio web malicioso realizando acciones no deseadas en nombre de un usuario en su sitio web. Se puede obtener más información en https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF).

La etiqueta mencionada genera un campo oculto con un código similar al siguiente:

Por defecto, Django verifica el token CSRF en todas las solicitudes tipo POST. Es importante recordar la inclusión de las etiquetas csrf_token en todos los formularios enviados vía POST.

Va a modificar la plantilla blog/post/detail.html y añadir el siguiente link para compartir la URL del artículo después de la variable {{ post.body | linebreaks }}:

Es necesario recordar que está construyendo URLs de forma dinámica usando la etiqueta {% url %} ofrecida por Django. Está utilizando el espacio de nombres blog y la URL post_share, y se pasa como parámetro el identificador del artículo para conformar la URL completa.

Va a revisar lo que ha realizado arrancando el servidor de desarrollo con python manage.py runserver y abriendo la URL http://127.0.0.1:8000/blog/ en el navegador. Seleccione cualquier título de artículo para visitar el detalle. Bajo el contenido del artículo, debería ver el enlace que ha añadido como se aprecia en las imágenes:

Si pincha en el enlace Share this post, le redirige a la página que incluye el formulario de compartición vía email de artículos.

Los estilos CSS para el formulario están incluidos en el código de ejemplo en el fichero static/css/blog.css. Tras seleccionar el botón Send e-mail, el formulario se envía y valida. Si todos los campos contienen información válida, obtendrá un mensaje de confirmación como este:

Si la información de entrada no es válida, el formulario se vuelve a visualizar incluyendo los errores de validación:

Hay que tener en cuenta que los navegadores modernos evitan el envío de información errónea o vacía. Esto se debe a que el navegador realiza una validación previa en base al tipo de campo y sus restricciones. En este caso, el formulario no será enviado y el formulario mostrará sus propios mensajes de error para los campos incorrectos.

El formulario para compartir artículos vía email está terminado. Lo siguiente es crear un sistema de comentarios para el blog.

Crear un sistema de comentarios

Ahora va a elaborar un sistema de comentarios para el blog, donde los usuarios puedan comentar los artículos. Para crear este sistema, es necesario realizar los siguientes pasos:

1. Crear un modelo para guardar los comentarios.

2. Crear un formulario para enviar comentarios y validar la entrada de datos.

3. Añadir una vista que procese el formulario y guarde el nuevo comentario en la base de datos.

4. Editar la plantilla de detalle de artículos para mostrar un listado de comentarios y el formulario para añadir nuevos.

Empezará por crear un modelo para almacenar los comentarios. Para ello va a editar el fichero models.py de la aplicación blog con el siguiente código:

Este es el modelo Comment. Contiene una ForeignKey para asociar un comentario con un único artículo. Esta relación muchos a uno es definida en el modelo Comment porque cada comentario estará constituido por un único artículo, y cada artículo puede tener muchos comentarios. El atributo related_name permite nombrar el atributo que compone esta relación, desde el lado del objeto referenciado. Después de definirlo, puede recuperar los artículos de una instancia de comentario usando comment.post, y recuperar todos los comentarios a un artículo con post.comments.all(). Si no usara el parámetro related_name, Django usará el nombre del modelo en minúsculas seguido de _set (es decir, comment_set) para nombrar el gestor de la relación inversa.

Para obtener más información de las relaciones muchos a uno puede visitar https://docs.djangoproject.com/en/2.0/topics/db/examples/many_to_one/.

Ha añadido el campo booleano active para poder desactivar comentarios inapropiados de manera manual. El campo created lo utilizará para ordenar los comentarios de manera cronológica por defecto.

El nuevo modelo Comment que acaba de crear no está sincronizado con la base de datos. Va a ejecutar el siguiente comando para generar una nueva migración que refleje la creación de un nuevo modelo:

A continuación debería ver por salida:

Django ha generado el fichero 0002_comment.py dentro del directorio migrations/ de la aplicación blog. Ahora es necesario crear un esquema y aplicar los cambios a la base de datos. Para ello ejecute:

La salida obtenida debería ser semejante a esta:

La migración que acaba de crear se ha aplicado, apareciendo la tabla blog_comment.

Ya puede añadir el nuevo modelo al panel de administración para poder gestionar comentarios a través de esta sencilla interfaz. Abra el fichero admin.py de la aplicación blog, importe el modelo Comment y añada la clase ModelAdmin:

Arranque el servidor con el comando python manage.py runserver y abra la URL http://127.0.0.1:8000/admin/ en el navegador. En este punto debería aparecer la sección BLOG como se muestra en la imagen:

El modelo está registrado en el panel de administración y puede gestionar instancias Comment a través de la interfaz.

Crear formularios de un modelo

Seguirá necesitando elaborar un formulario para que los usuarios puedan escribir comentarios de un artículo del blog. Recuerde que Django dispone de dos clases base para construir formularios: Form y ModelForm. Ya ha utilizado la primera anteriormente para permitir a los usuarios compartir artículos vía email. En este caso, va a utilizar la clase ModelForm para crear un formulario dinámicamente a partir del modelo Comment. Edite el fichero forms.py de la aplicación blog con las siguientes líneas:

Para crear un formulario a partir de un modelo, necesitamos indicar qué modelo usar para la construcción del formulario en la clase Meta de este último. Django inspecciona el modelo y construye un formulario dinámicamente por usted. Cada tipo de campo del modelo tiene su correspondiente tipo de campos de formulario. La manera en que defina los campos de los modelos se tendrá en cuenta para la validación de los formularios. Por defecto, Django construye un campo de formulario por cada campo del modelo. Sin embargo, puede indicar explícitamente qué campos quiere incluir en el formulario con la lista fields o definir qué campos quiere excluir con la lista exclude. Para el formulario CommentForm, ha usado los campos name, email y body porque estos son los campos que los usuarios podrán rellenar.

Funcionamiento de ModelForms en vistas

La idea consiste en utilizar la vista de detalle de artículos para instanciar un formulario y procesarlo de una forma sencilla. Para ello, edite el fichero views.py, importe el modelo Comment y el formulario CommentForm, y modifique la vista post_detail del siguiente modo:

Revise lo que ha añadido a la vista. La vista post_detail muestra el artículo y los comentarios del mismo. Se ha añadido un QuerySet para recuperar todos los comentarios activos para este artículo con la siguiente sentencia:

Este QuerySet está construido a partir del objeto post. Se ha utilizado el gestor para objetos relacionados que se define como comments a través del atributo related_name de la relación con el modelo Comments.

Se ha usado la misma vista para permitir a los usuarios añadir nuevos comentarios. Por esta razón se inicializa la variable new_comment con valor None. Esta variable la utilizará con la creación de un nuevo comentario. En caso de que la petición sea tipo GET, se instancia un formulario con comment_form = CommentForm(). Si la petición es de tipo POST, la instanciación del formulario se realiza con los datos enviados y la validación se lleva a cabo con el método is_valid(). Si el formulario es inválido, en la renderización de la plantilla aparecerán la validación de errores. Si, por el contrario, es válido, se realizan las siguientes acciones:

1. Se crea un nuevo objeto Comment llamando al método save() del formulario y asignándolo a la variable new_comment:

El método save() crea una instancia del modelo con el que el formulario está relacionado y lo guarda en la base de datos. Si invocamos el método con el parámetro commit=False, se creará la instancia, pero no se salvará en la base de datos. Esto es útil cuando se quiere modificar el objeto antes de guardarlo definitivamente.

El método save() está disponible para ModelForm pero no para Form, ya que este último no está relacionado con un modelo.

2. Asignará el artículo actual al comentario que acaba de crear con:

3. Por último, guardará el nuevo comentario a través de la llamada al método save():

La nueva vista está lista para mostrar y procesar comentarios.

Añadir comentarios a la plantilla de detalle de artículos

Una vez creada la funcionalidad para gestionar comentarios para un artículo, es necesario adaptar la plantilla post/detail.html para:

• Mostrar el total de comentarios relacionados con artículos.

• Mostrar la lista de comentarios.

• Mostrar un formulario a los usuarios para añadir un comentario nuevo.

Lo primero que hará será añadir el número total de comentarios. Para ello editará la plantilla post/detail.html y añadirá el siguiente contenido al bloque content:

Para ello está usando el ORM de Django en la plantilla, ejecutando el método count() del QuerySet comments. El lenguaje de plantillas de Django no hace uso de paréntesis a la hora de llamar a métodos o funciones. El tag {% with %} permite asignar un valor a una nueva variable que estará disponible para usar dentro del bloque, hasta la etiqueta {% endtag %}.

La etiqueta de plantilla {% with %} es útil para evitar múltiples ejecuciones costosas dentro de la misma plantilla, por ejemplo, sobre la base de datos.

Utilizará el filtro de plantilla pluralize para mostrar el plural de la palabra comment en función del valor de total_comments. Este filtro funciona para múltiples idiomas, incluido el español. Los filtros de plantilla toman el valor de la variable a la que se aplican y la utilizan como entrada para generar una salida. Esto se tratará en el capítulo 3, Extensiones para el blog.

El filtro de plantilla pluralize devuelve una cadena con la letra “s” si el valor de entrada es diferente de 1. Los posibles textos renderizados serán 0 comments, 1 comment o N comments. Django incluye múltiples etiquetas y filtros de plantilla que nos ayudan a mostrar la información del modo en que necesitará.

A continuación, incluirá la lista de comentarios. Para ello añadirá en la plantilla post/detail.html las siguientes líneas:

La etiqueta {% for %} permite iterar sobre los comentarios. En caso de que la lista comments esté vacía, mostrará un comentario por defecto indicando al usuario que todavía no hay comentarios. Con la variable {{ forloop. counter }} enumerará los comentarios. Esta variable contiene un contador de bucle, indicando la iteración en que se encuentra. Por último, se muestra el nombre del usuario que realizó el comentario, la fecha y las primeras letras del cuerpo del comentario.

Para terminar, queda por mostrar un mensaje indicando que todo ha ido correctamente en caso de procesar bien el formulario, o el formulario en sí. Va a añadir las siguientes líneas al código anterior:

En caso de que el objeto new_comment exista, se muestra un mensaje satisfactorio indicando que el comentario se ha creado correctamente. En caso contrario, se muestra el formulario con un elemento párrafo <p> por cada campo, incluyendo el token CSRF necesario para las acciones POST. Va a abrir http://127.0.0.1:8000/blog/ en el navegador y pulsar sobre el título de un artículo para ver la página de detalle. Debería ver algo similar a esto:

Si añade varios comentarios a través del formulario, estos deberían aparecer bajo el artículo en orden cronológico:

Va a ir a http://127.0.0.1:8000/admin/blog/comment/ en el navegador. Verá la página de administración con la lista de comentarios que ha creado. Seleccione uno para editarlo. Desactive el check Active y pulse sobre el botón Save. El sistema le redirigirá a la lista de comentarios, y podrá ver que la columna Active se encuentra desactivada para el comentario que acaba de modificar. La pantalla debería verse de un modo similar a:

Si vuelve a la página de detalle del artículo, podrá ver que el comentario desactivado no se muestra. Tampoco se tiene en cuenta para la contabilización de comentarios. Gracias al campo active, puede desactivar comentarios inapropiados y evitar mostrarlos en los artículos.

Añadir funcionalidad de etiquetado

Tras implementar el sistema de comentarios, va a crear un modo de etiquetar los artículos. Esto lo hará integrando aplicaciones de Django de terceros en los proyectos. El módulo django-taggit es una aplicación reutilizable que ofrece un modelo Tag y un gestor que facilita añadir etiquetas a cualquier modelo. En https://github.com/alex/django-taggit puede echar un vistazo al código.

Lo primero que tiene que hacer es instalar django-taggit a través de pip con el siguiente comando:

Tras la instalación, va a editar el fichero settings.py del proyecto mysite e incluir la aplicación taggit en la lista INSTALLED_APPS:

A continuación, abra el fichero models.py de la aplicación blog y añada el gestor TaggableManager que ofrece django-taggit para relacionarlo con el modelo Post:

El gestor tags permite añadir, recuperar y eliminar etiquetas de un objeto Post.

Para generar las migraciones, va a ejecutar el siguiente comando para llevar a cabo los cambios en el modelo:

Obteniendo la siguiente salida:

Ahora, ejecute el siguiente comando para crear las tablas necesarias para los modelos de django-taggit y sincronizar los cambios que ha realizado en Post:

Obtendrá una salida indicando que las migraciones se han aplicado:

Con esto, la base de datos está lista para utilizar los modelos de django-taggit. A continuación, se detalla cómo usar el gestor tags. Para ello abra una consola y ejecute el comando python manage.py shell. Una vez tengamos el intérprete listo, ejecute las siguientes líneas de código para recuperar uno de los artículos (el que tiene id 1):

Ahora va a añadir algunas etiquetas y verificar que se han incorporado correctamente:

Por último, eliminará una etiqueta y verificar la lista de etiquetas:

Va a verificar esta misma información desde el panel de administración. Para ello, ejecute el servidor de desarrollo con python manage.py runserver y abra la URL http://127.0.0.1:8000/admin/taggit/tag/ en el navegador. Puede ver la página con la lista de objetos Tag de la aplicación taggit:

Vaya a http://127.0.0.1:8000/admin/blog/post/ y pulse sobre un artículo para poder editarlo. Los artículos ahora tienen un nuevo campo Tags, el cual se puede editar con facilidad:

Lo siguiente es modificar el blog para mostrar las etiquetas. Edite la plantilla blog/post/list.html y añada el siguiente código HTML tras el título del artículo:

El filtro de plantilla join funciona como el método join() de la clase string en Python, permitiendo concatenar elementos con la cadena dada. Navegará hasta http://127.0.0.1:8000/blog/ y debería ver algo similar a esto:

Va ahora a editar la vista post_list para poder mostrar a los usuarios todos los artículos con una etiqueta específica. Para ello, edite el fichero views.py de la aplicación blog, importando el modelo Tag de django-taggit y modificando la vista para que acepte, de forma opcional, un filtrado por etiqueta:

La vista post_list ahora funciona del siguiente modo:

1. Recupera un parámetro opcional, tag_slug, que por defecto tiene valor None. Este parámetro vendrá en la URL.

2. En la vista, se construye un QuerySet inicial, recuperando todos los artículos publicados. En caso de recibir un nombre de etiqueta, se recupera el objeto Tag correspondiente a través del atajo de Django get_object_or_404().

3. Con el objeto, filtrará la lista de artículos por aquellos que contengan la etiqueta mencionada. Dado que es una relación muchos-a-muchos, es necesario filtrar por las etiquetas contenidas en una lista, que, en este caso, es de un solo elemento.

Es necesario recordar que los QuerySets son perezosos, es decir, se ejecutan contra la base de datos solo cuando itere sobre la lista de artículos, cuando trate de renderizarlos o procesarlos de algún modo.

Por último, va a modificar la función render() abajo de la vista, para pasar la variable tag a la plantilla. La vista quedará finalmente así:

Queda modificar el fichero urls.py de la aplicación blog, comentar el patrón de URL de la vista basada en clase PostListView, y descomentar la vista post_list:

Añada también el patrón de URL para listar los artículos por etiqueta:

Como se puede ver, los dos patrones de URL utilizan la misma vista, pero los ha nombrado de un modo diferente. El primer patrón llama a la vista post_list sin ningún parámetro opcional, mientras que el segundo llama a la vista con el parámetro tag_slug. Esto indica que el parámetro es de tipo slug, de forma que sea una cadena de texto en minúsculas, formado por letras y/o números ASCII, y que pueda contener guiones medios y/o bajos.

Ahora que vuelve a usar la vista post_list, es necesario modificar la paginación para usar el objeto post dentro de la plantilla blog/post/list.html:

Añada las siguientes líneas antes de bucle {% for %}:

Cuando los usuarios accedan al blog verán la lista de todos los artículos, y si filtran por etiqueta, verán los que contengan la etiqueta seleccionada. Va ahora a modificar la forma en que se muestran las etiquetas:

Ahora iterará sobre todas las etiquetas del artículo, mostrando por cada una de ellas un enlace para filtrar los artículos por dicha etiqueta. Ha construido la URL con {% url "blog:post_list_by_tag" tag.slug %}, con el nombre de la URL y la etiqueta slug como parámetros.

Abra http://127.0.0.1:8000/blog/ en el navegador y pinche sobre cualquier etiqueta. Esto nos mostrará todos los artículos con dicha etiqueta:

Recuperar artículos por similitud

Una vez implementado el sistema de etiquetado en el blog, puede utilizarlo para hacer cosas interesantes con él. A través de las etiquetas, puede clasificar de forma muy específica el contenido del blog. Artículos de temas relacionados tendrán etiquetas comunes. Va a construir una funcionalidad para mostrar artículos similares según la cantidad de etiquetas en común. De esta forma, cuando un usuario lea un artículo, puede sugerirle leer otros con contenido relacionado.

Para recuperar los artículos similares a otro artículo, es necesario realizar los siguientes pasos:

1. Recuperar todas las etiquetas del artículo actual.

2. Recuperar todos los artículos que contengan algunas de las etiquetas previamente recuperadas.

3. Excluir el artículo actual de la lista anterior para evitar recomendar el mismo artículo.

4. Ordenar los resultados por número de etiquetas en común con el artículo actual.

5. En caso de que uno o más artículos contengan el mismo número de etiquetas, se recomienda el más reciente.

6. Limitar la consulta al número de artículos que quiere recomendar.

Estos pasos se trasladan a consultas complejas que incluirá en la vista post_detail. Va a editar el fichero views.py y añadir la siguiente línea de importación al comienzo del fichero:

Esta es la función de agregación Count del ORM de Django. Esta función permite realizar contabilizaciones agregadas de etiquetas. El módulo django.db.models incluye las siguientes funciones de agregación:

• Avg: The value average

• Max: The maximum value

• Min: The minimum value

• Count: The objects count

Para profundizar sobre la agregación visita se recomienda visitar https://docs.djangoproject.com/en/2.0/topics/db/aggregation/.

Añadirá las siguientes líneas dentro de la vista post_detail antes de la función render(), con el mismo nivel de indentación:

El código anterior funciona del siguiente modo:

1. Se recupera una lista Python de identificadores de las etiquetas del artículo actual. El QuerySet values_list() devuelve tuplas con los valores de los campos especificados. Al pasar el parámetro flat=True, aplana la estructura, resultando: [1, 2, 3, ...].

2. Se recuperan todos los artículos que contengan cualquiera de las etiquetas, excluyendo el artículo actual.

3. Utilizará la agregación Count para generar un campo calculado (same_tags) que contiene el número de etiquetas compartidas de cada artículo con el actual.

4. Se ordenan los resultados por número de etiquetas compartidas (orden descendente) y por el campo publish, para mostrar primero los artículos más recientes en caso de coincidir en número de etiquetas en común. Se limitará a recuperar solo los 4 primeros artículos.

Añadirá el objeto similar_posts al contexto del diccionario de la función render() del siguiente modo:

Va ahora a editar la plantilla blog/post/detail.html y añadir el siguiente código antes de los comentarios del artículo:

Ahora nuestra página de detalle de artículo debería ser semejante a:

Ahora ya puede recomendar artículos a los usuarios. django-taggit también tiene un gestor, similar_objects(), que puede utilizar para recuperar objetos similares que contengan etiquetas en común. Para obtener más información sobre cómo funciona el gestor visite https://djangotaggit.readthedocs.io/en/latest/api.html.

Como último detalle, también puede añadir la lista de etiquetas de la vista de detalle a la plantilla blog/post/list.html.

Resumen

En este capítulo ha aprendido cómo funcionan los formularios estándar de Django así como los formularios de modelo. Ha creado un sistema para compartir contenido del sitio web por email y otro para realizar comentarios. Añadió el etiquetado a los artículos del blog, integrando una aplicación, y construyó consultas complejas para recuperar objetos por similitud.

En el siguiente capítulo aprenderá a crear filtros y etiquetas de plantilla personalizados. También construirá un mapa del sitio y creará contenido para el blog. Por último, implementará un sistema de búsqueda de texto.

Конец ознакомительного фрагмента.

Текст предоставлен ООО «ЛитРес».

Прочитайте эту книгу целиком, купив полную легальную версию на ЛитРес.

Безопасно оплатить книгу можно банковской картой Visa, MasterCard, Maestro, со счета мобильного телефона, с платежного терминала, в салоне МТС или Связной, через PayPal, WebMoney, Яндекс.Деньги, QIWI Кошелек, бонусными картами или другим удобным Вам способом.