Kitabı oku: «Django 2», sayfa 3
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.