logo cosasdedevs

Cómo customizar el manager de un modelo con Django

Cómo customizar el manager de un modelo con Django

My Profile
Ene 31, 2020

Puede que como a mí, alguna vez os haya pasado que cuando habéis usado un ORM, habréis echado de menos la posibilidad de añadir nuevas funcionalidades adaptadas a un modelo en concreto. Tengo buenas noticias, Django soluciona esto dando la posibilidad de customizar el manager del modelo, así como añadir distintos managers que se adapten a tus necesidades.

Bueno, bueno, estoy hablando mucho de managers pero para el que no tenga claro que son, esta es la definición de la documentación oficial, también podéis ver más información en la documentación oficial de Django.

Un manager es la interfaz a través de la cual se proporcionan operaciones de consulta de base de datos a los modelos de Django. Existe al menos un Manager para cada modelo en una aplicación Django.

Para este ejemplo vamos a usar un modelo similar al que usamos para la paginación por paises, aunque esta vez añadiremos un campo más llamado región que será numérico y corresponderá a cada uno de los continentes.

from django.db import models
from django.utils.translation import gettext_lazy as _

class Country(models.Model):
    """Country model."""
    
    iso_code = models.CharField(max_length=2,unique=True)
    name = models.CharField(max_length=255,unique=True)
    region = models.CharField(max_length=1, null=True, choices=[
        ('1', _('Asia')), 
        ('2', _('Europe')),
        ('3', _('Africa')),
        ('4', _('Oceania')),
        ('5', _('America')),
        ('6', _('Antartica'))
    ])

    class Meta:
        ordering = ('name',)

    def __str__(self):
        return self.name

Imaginad que tenemos un modelo como este en el que por alguna extraña razón se decidió que el continente se guardaría con un identificador y que además no tendríamos ninguna relación con otra tabla para identificar la correspondencia. Si tenemos que filtrar por continente sería dificil recordar la correspondencia y tendríamos que estar abriendo el modelo cada vez que lo tuvieramos que filtrar. Pues bien, esto lo vamos a solucionar customizando los managers. Justo antes de la declaración del modelo, añadiremos el siguiente código:

class CountriesAsia(models.Manager):
    def get_queryset(self):
        return super().get_queryset().filter(region='1')

Lo que estamos haciendo es crear una clase que extenderá de models.Manager y en el método get_queryset añadirá el filtro por región a cualquier query que se lance desde este manager.

Después dentro de la clase del modelo añadiremos el nuevo manager para que posteriormente podamos utilizarlo.

class Country(models.Model):
    """Country model."""
    
    iso_code = models.CharField(max_length=2,unique=True)
    name = models.CharField(max_length=255,unique=True)
    region = models.CharField(max_length=1, null=True, choices=[
        ('1', _('Asia')), 
        ('2', _('Europe')),
        ('3', _('Africa')),
        ('4', _('Oceania')),
        ('5', _('America')),
        ('6', _('Antartica'))
    ])

    objects = models.Manager()
    asia_objects = CountriesAsia()

    class Meta:
        ordering = ('name',)

    def __str__(self):
        return self.name

Aqui lo que hemos hecho es añadir el manager por defecto objects y el nuevo manager asia_objects en el que todas las consultas que realicemos nos añadirá el filtro adicional por región.

Para usar nuestro nuevo manager sustituiremos objects por asia_objects y añadimos el tipo de búsqueda que queramos realizar (all, filter, get, etc...). Si por ejemplo usamos Country.asia_objects.all() nos mostrará todo el listado de paises de Asia, pero si queremos todos los paises independientemente de su continente tendríamos que usar la llamada de siempre (Country.objects.all())

Lo mejor de esto es que podemos crear varios managers. Para todos los continentes quedaría así:

from django.db import models
from django.utils.translation import gettext_lazy as _


class CountriesAsia(models.Manager):
    def get_queryset(self):
        return super().get_queryset().filter(region='1')

class CountriesEurope(models.Manager):
    def get_queryset(self):
        return super().get_queryset().filter(region='2')

class CountriesAfrica(models.Manager):
    def get_queryset(self):
        return super().get_queryset().filter(region='3')

class CountriesOceania(models.Manager):
    def get_queryset(self):
        return super().get_queryset().filter(region='4')

class CountriesAmerica(models.Manager):
    def get_queryset(self):
        return super().get_queryset().filter(region='5')

class CountriesOther(models.Manager):
    def get_queryset(self):
        return super().get_queryset().filter(region='6')

class Country(models.Model):
    """Country model."""
    
    iso_code = models.CharField(max_length=2,unique=True)
    name = models.CharField(max_length=255,unique=True)
    region = models.CharField(max_length=1, null=True, choices=[
        ('1', _('Asia')), 
        ('2', _('Europe')),
        ('3', _('Africa')),
        ('4', _('Oceania')),
        ('5', _('America')),
        ('6', _('Antartica'))
    ])

    objects = models.Manager()
    asia_objects = CountriesAsia()
    europe_objects = CountriesEurope()
    africa_objects = CountriesAfrica()
    america_objects = CountriesAmerica()
    oceania_objects = CountriesOceania()
    others_objects = CountriesOther()

    class Meta:
        ordering = ('name',)

    def __str__(self):
        return self.name

Como siempre dejo el código del proyecto en mi github para que podáis ver el ejemplo. Para probarlo podéis usar la librería shell_plus que ya está añadida al proyecto; solo tendríais que descargarlo e instalarlas con pip install -r requirements.txt

Para lanzar shell_plus y ver el ejemplo del funcionamiento podemos hacerlo de esta forma:

python manage.py shell_plus --ipython

Se importarán las librerías y nuestro modelo countries y ya solo tendríamos que lanzar las siguientes líneas para verlo en acción:

countries = Country.asia_objects.all()
for country in countries:
	print(country.name)

Como veis es bastante sencillo customizar los managers de un modelo. Cualquier duda podéis ponerla en los comentarios.

Espero que os ayude ;)

804 vistas

Nos tomamos en serio tu privacidad

Utilizamos cookies propias y de terceros para mejorar la experiencia del usuario a través de su navegación. Si pulsas entendido aceptas su uso. Ver política de cookies.

🐍 Sígueme en Twitter

Si te gusta el contenido que subo y no quieres perderte nada, sígueme en Twitter y te avisaré cada vez que cree contenido nuevo 💪
Luego ¡Te sigo!