Índice del contenido

Managers o manejadores personalizados en Django

Managers o manejadores personalizados en Django

Un Manager (o manejador) es la interfaz a través de la cual se proveen las operaciones de consulta o queries de la base de datos a los modelos de Django. Sí, me refiero a ese objects que va después del nombre de tu modelo; TuModelo.objects.all() y Tumodelo.objects.filter(). Todos los modelos de Django tienen al menos un manager. Cada vez que usas el manejador de objetos (me referiré a él como manager de aquí en adelante) en una consulta a la base de datos usando el ORM de Django estás haciendo uso de su object manager predeterminado. Estos managers en Django pueden personalizarse para modificar los objetos que devuelve una consulta y podemos personalizarlos a nuestro gusto.

Antes de empezar, si no sabes lo básico de Django puedes empezar con la guia definitiva de Django

Por otro lado, si estás buscando optimizar tu aplicación de Django, probablemente mi entrada donde hablo sobre como mejorar el rendimiento de apps lentas de Django te sirva más.

El object manager de Django

Si has usado el ORM de Django, seguramente ya habrás usado el manager por defecto. Objects es el nombre del manejador por defecto y se encarga de devolver todos los objetos de un modelo de Django.

Videogame.objects.all()

Modificando el manager por defecto

Quizás queremos tener dos managers, uno que devuelva todos los objetos y otro que devuelva los objetos más recientes, o los objetos creados por un usuario en particular, o los objetos filtrados por un termino.

Empecemos por modificar el nombre del manager que viene por defecto, para hacerlo basta con que lo asignemos al objeto Manager de models.

from django.db import models

  class Videogame(models.Model):
  ...#
      stem = models.Manager() #Esto te permitira llamar Videogame.stem.all() en lugar de Videogame.objects.all()

¿Y esto para que? Pues que ahora podemos llamar al object manager de una manera diferente, lo cual puede mejorar la legibilidad de nuestro código, pero no es la razón más importante.

Videogame.stem.all()# En lugar de Videogame.objects.all()

Agregando métodos a un manager de Django

Un manager personalizado nos permite agregarle nuevos métodos, que le otorgarán comportamientos únicos . ¿Cómo cuales? puedes filtrar los resultados de alguna búsqueda, limitar los resultados de acuerdo al usuario, un rango de fechas, un número de resultados, lo que tú prefieras.

Mira este ejemplo a continuación, instanciamos un nuevo manager llamado VideogameManager, el cual hereda de models.Manager. Le agregamos un método llamado contar_títulos que se encargará de contar los resultados para una determinada búsqueda, nada muy complicado, simplemente concatenamos un filter con una consulta, como si se tratara de cualquier búsqueda.

Ya que tenemos este nuevo manager con el método contar_titulos, reemplazamos la propiedad objects de nuestro modelo Videogame por una instancia del manager que acabamos de crear.

from django.db import models

  class VideogameManager(models.Manager):
      def contar_titulos(self, keyword):
          return self.filter(titulo__icontains=keyword).count()
   #self se refiere al manager en sí mismo
  class Videogame(models.Model):
    
      objects = VideogameManager() #Renombra al manager por defecto aquí se usa objects para ser consistente

Ahora nuestro manager predeterminado, objects, cuenta con un método llamado contar_titulos que podemos usar como si formara parte del ORM original de Django.

Videogame.objects.contar_titulos('fantasy')

Modificando los QuerySets iniciales del Manager

Un QuerySet base de un Manager devuelve todos los objetos en el sistema. Pero, ¿y si solo nos interesan ciertos datos? Imagínate que la tienda en linea tiene una base de datos de todos los videojuegos, pero, como somos unos geeks básicos, le dimos a la compañia squarenix una sección especial.

Si escribimos consultas individuales personalizadas para cada queryset de esa sección quedarían algo así:

Videogames.objects.filter(company="squarenix").filter(titulo__icontains="Fantasy")
# ...
Videogames.objects.filter(company="squarenix").filter(descripcion__icontains="Aventura")
# ...
Videogames.objects.filter(company="squarenix").filter(genero="RPG")

Como ya sabes, lo anterior repite demasiado código, violando la máxima de DRY.

Podemos reemplazar el QuerySet base, sobreescribiendo el método Manager.get_query_set() para que la queryset que obtengamos por defecto haga el filtrado por el nombre de la compañia.

  from django.db import models

  # Primero, definimos una subclase para el Manager.
  class SquarenixManager(models.Manager):
      def get_query_set(self):
          return super(SquarenixManager, self).get_query_set().filter(company='squarenix')

  # Despues lo anclamos al modelo Videogame explícitamente.
  class Videogame(models.Model):
      # ...
      objects = models.Manager() # El manager predeterminado.
      squarenix_videogames = SquarenixManager() # Nuestro manager

Nota como ahora tenemos dos managers. Un Modelo puede definir varios manager, el primer manager que aparezca es el manager por omisión (en el ejemplo de arriba es objects), el cual será usado por Django internamente para otras características especiales.

Al ejecutar el manager devolverá solo los libros que tengan como compañia Squarenix y, además, puede usar todos los métodos de QuerySet sobre él.

Videogame.objects.all() # Devuelve todos los videojuegos
Videogame.squarenix_videogames.all() # Devuelve solo los videojuegos de squarenix
Videogame.squarenix_videogames.filter(titulo__icontains='Kingdom Hearts') #Devuelve los videojuegos de squarenix cuyo título contenga Kingdom Hearts

Y es todo. Ahora que sabes eso puedes crear tantos managers como quieras que te den tantas búsquedas filtradas como necesites.

Si quieres profundizar más en el tema de los managers revisa la documentación oficial de Django.

Eduardo Zepeda
Desarrollador web, entusiasta de los sistemas GNU/Linux y el Software Libre. Py, Ts y Go, pero abierto a otras opciones como el Rustaceanismo. Creo en las bondades de las criptodivisas más allá de la especulación monetaria.
Leer más