in django orm ~ read.

Vous reprendrez bien un peu d'ORM ?

Pré requis

Commençons cet article par quelques rappels histoire de savoir de quoi
on va parler ici.

ORM

Un object-relational mapping (mapping objet-relationnel en français)
est une technique de programmation informatique qui crée l'illusion
d'une base de données orientée objet à partir d'une base de données
relationnelle en définissant des correspondances entre cette base de
données et les objets du langage utilisé. On pourrait le désigner par
« correspondance entre monde objet et monde relationnel ». —
Wikipédia

Un ORM est donc un outil qui permet d'associer un ou plusieurs objets, à
une ou plusieurs tables d'une base de données relationnelle. C'est le
parallèle entre votre modèle de données et vos données telles qu'elles
sont stockées.

Django

Django est un framework web écrit en Python. Il vous fournit tout (ou
presque) ce dont vous avez besoin pour construire une application web en
Python:

  • un routeur d'url;
  • une gestion de l'authentification;
  • une gestion des sessions;
  • un moteur de template;
  • un ORM;
  • un système de middleware;
  • et j'en passe

Utilisez la puissance de l'ORM de Django

Je ne vais pas passer sur les méthodes de base de l'ORM, la
documentation
s'en charge déjà très bien elle-même. Passons plutôt à une utilisation
un peu plus avancée et du coup plus sympa. Intéressons nous aux méthodes
annotate et aggregate.

Mise en place

class Student(models.Model):
    firstname = models.CharField(max_length=30)
    lastname = models.CharField(max_length=30)

    def __unicode__(self):
        return '{} {}'.format(self.firstname, self.lastname)


class Grade(models.Model):
    student = models.ForeignKey(Student)
    value = models.FloatField()

Annotate

Annotate permet d'ajouter, à la volée et à chacun des éléments
retournés par la requête, un ou plusieurs attributs. De ce fait, il sera
possible de filtrer, trier, ou faire des actions d'agrégation sur ces
attributs en base de données.

Regardons quelques exemples pour illustrer ça :

import random
from django.db.models import Avg
s1 = Student.objects.create(firstname='john', lastname='doe')
s2 = Student.objects.create(firstname='jane', lastname='doe')
for value in [random.randint(0, 20) for i in xrange(0, 30)]:
    Grade(student=s1, value).save()
for value in [random.randint(0, 20) for i in xrange(0, 30)]:
    Grade(student=s2, value).save()
# ajouter la moyenne des notes de chaque élève
students = Student.objects.annotate(avg=Avg('grade__value'))
print(students[0].avg)  # 8.23
print(students[1].avg)  # 11.3
print(students.filter(avg__gte=10))  # <QuerySet [<Student: john doe>]>

On peut également ajouter un attribut avec une valeur arbitraire

from django.db.models import ExpressionWrapper, Value, IntegerField
students = Student.objects.annotate(max_abs=ExpressionWrapper(Value(3),
                                                             output_field=IntegerField()))
print(students[0].max_abs)  # 3
print(students[1].max_abs)  # 3

Mais annotate nous permet de faire des choses plus complexes, comme
concaténer plusieurs champs.

from django.db.models import F, ExpressionWrapper, Value
from django.db.models.functions import Concat
students = students.annotate(fullname=ExpressionWrapper(Concat(F('firstname'), Value(' '), F('lastname')),
                                                        output_field=CharField()))
print(students[1].fullname)  # jane doe

Ici, c'est l'objet F() qui permet de faire référence à un attribut de
l'élément en cours de traitement.

Admettons maintenant que vous vouliez, pour chaque élément, indiquer si
l'élève est reçu ou non (c'est à dire, est-ce que sa moyenne est
supérieur ou égale à 10). Utilisons Case() et `When()`:

from django.db.models import Case, When
students_with_avg = Student.objects.annotate(avg=Avg('grade__value'))
# ajoutons l'admissibilité
admissible = ExpressionWrapper(Value('admis'), output_field=CharField())
non_admissible = ExpressionWrapper(Value('non admis'), output_field=CharField())
students = students_with_avg.annotate(admissible=Case(When(avg__gte=10, then=admissible),
                                                      default=non_admissible))
students = students.annotate(fullname=ExpressionWrapper(Concat(F('firstname'), Value(' '), F('lastname')),
                                                        output_field=CharField()))

print(dict(students.values_list('fullname', 'admissible')))
# output: {'john doe': 'non admis',
#          'jane doe': 'admis'}

Aggregate

Aggregate quant à elle, s'applique sur l'ensemble des éléments
retournés par la requête. Elle permet d'exécuter une fonction (Sum, par
exemple) sur le résultat de la requête. Voyons quelques exemples.

from django.db.models import Max, Min
# trouvons la moyenne générale
students_with_avg = Student.objects.annotate(student_avg=Avg('grade__value'))
global_avg = students_with_avg.aggregate(Avg('sutdent_avg'))
print(global_avg)  # {'student_avg__avg': 9.765}
# trouvons la note la plus basse
print(Grade.objects.aggregate(min_value=Min('value'),
                              max_value=Max('value')))
# output: {'min_value': 3, 'max_value': 18}

Voilà déjà de quoi jouer un peu avec l'ORM de Django.