Les Managers, est-ce vraiment indispensable ?

Indispensable non mais très utile oui, voyez ça comme un formidable raccourci, dans l'écriture de vos requêtes, hum queryset pardon.

Je reprends volontairement un exemple proche de la doc officielle. Prenons le cas de la gestion de "Personne" class Person(models.Model): avec tout un tas de données confidentielles. Si je souhaite obtenir la liste complète j'utilise tout simplement la méthode suivante Person.objects.all(). Bon et bien maintenant je souhaite obtenir une liste de retraités, facile il suffit de regarder l'âge de chaque personne. Ce qui pourrait ressembler classiquement à : Person.objects.filter(age__gte = 60)

Ceci fonctionne très bien jusqu'à la réforme des retraites et l'obligation du départ à 67 ans.. Ok il suffit de changer 60 par 67 et "astalavista baby". Oui mais niveau maintenance c'est la misère, surtout si on utilise ce filtre au quatre coin de l'application. Magie avec les fameux managers, le queryset/requête devient : Person.retired.all().

Je répète :

  • Person.objects.filter(age__gte = 60) devient Person.retired.all().

En pratique, le code

Classe

Dans models.py

class Person(models.Model):
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=30)
    sex = models.CharField(max_length=1, choices=(('M', 'Male'), ('F', 'Female')))
    age = models.AgeField()
    
   objects = models.Manager() #pour pas que Django perde la tête
   retired = RetiredManager()  #nouveautés pour gérer les retraités



Managers

A placer dans models.py ou managers.py si vous êtes ordonnés !

class RetiredManager(models.Manager):
    def get_query_set(self):
        return super(RetiredManager, self).get_query_set().filter(age__gte = 60)

Queryset Manager, aller plus haut !

Pratique hein ? mais pas toujours .. Imaginer que par recoupement multiple, queryset croisés... "si si au bout d'un moment ça arrive", je récupére une liste de 30 personnes, et que parmi elles je souhaite extraire mes fameux retraités de sexe féminins, soyons fous.. J'aimerais beaucoup pouvoir écrire some_people.get_women().get_retired() ou some_people.get_retired().get_women(). Bref chainer mes filtres.

Remarque : some_people doit évidemment être un queryset de "Person"

Le code

Dans models.py :

class QuerySetManager(models.Manager):
    def get_query_set(self):
        return self.model.QuerySet(self.model)
    def __getattr__(self, attr, *args):
        return getattr(self.get_query_set(), attr, *args)
class Person(models.Model):
 ..... tout le blabla précédent ....
objects = QuerySetManager()
..............................................
    class QuerySet(QuerySet):
        def get_retired(self):
            return self.objects.filter(age_gte = 60)
        def get_women(self):
            return self.objects.filter(sex='F')

Voila vous savez tout...

A la place d'un classique some_people.filter(sex="F").filter(age__gte = 60) on peut donc utiliser : some_people.get_women().get_retired() La maintenance est donc facilitée au prix parfois de la performance.