Apr 022010
 

Les queryset sont une des composantes importantes de Django. Comment en effet interagir avec la BD sans eux ?

Mais est ce que cet outil si important et si souvent utilisé est si bien connu que ça ?

Parce que tout le monde connait count(), filter(), all() et exclude(). Mais qu’n est-il des autres méthodes ? Perso, je suis le premier à aller dans la doc, pour revérifier si ce que je voudrais n’existe pas déjà…

C’est le pourquoi de ce billet, lister quelques méthodes ‘à connaître’ des querysets (et puis comme ça la prochaine fois que j’aurais besoin de vérifier un truc, je pourrais le faire en lisant du français et pas de l’anglais). (Ce n’est au final qu’une redite de la page de doc qui va bien, mais ça peut servir).

1- Les méthodes qui renvoient un queryset (ou assimilés)

annotate(*args, **kwargs)

une petite méthode bien pratique, qui permet de rajouter des colonnes calculées (en utilisant les classes d’agrégat Sum,Count, etc défini par django) pour chaque objets récupérés dans le queryset.

Et ça, c’est plutôt fort. Surtout que l’on peut, bien entendu, ‘traverser’ les foreign key
L’un des exemples de la doc, montre cela en calculant pour un magasin le prix minimum et maximum des livres en vente :

Store.objects.annotate(min_price=Min('books__price'), max_price=Max('books__price'))

values(*fields) et Values_list (*fields)

Deux méthodes plus que miam.
Values retourne un ValuesQuerySet qui est en fait un queryset composé d’une liste de dictionnaires au lieu d’une liste d’instance d’objet modèle. Chaque dictionnaire représente un objet, les paire clés / valeur représentant le nom de l’attribut (la key) et la valeur de l’attribut (sa valeur).
On peut passer à values un paramètre optionnel *fields, qui permet de spécifier la liste des noms d’attribut (des strings donc) que l’on veut récupérer.
Exemple :

>>>Blog.objects.values()
[{'id': 1, 'name': 'Beatles Blog', 'tagline': 'All the latest Beatles news.'}],
>>> Blog.objects.values('id', 'name')
[{'id': 1, 'name': 'Beatles Blog'}]

Deux choses importantes à se rappeler, values ne récupère rien pour les manytomany et dans le cas des FK, la clé du dico est le nom ‘vrai’ de l’attribut dans la table (souvent avec _id donc) et la valeur, la valeur de la PK de la FK. Et comme le voit dans l’exemple, si on veut passer le nom explicite de l’attribut de la fk, on peut au choix mettre ou pas le _id, le résultat est le même.

Exemple :

>>> Entry.objects.values()
[{'blog_id: 1, 'headline': u'First Entry', ...}, ...]

>>> Entry.objects.values('
blog')
[{'
blog': 1}, ...]

>>> Entry.objects.values('
blog_id')
[{'
blog_id': 1}, ...]

values_list c’est à peu près la même chose que values, sauf que c’est une liste de tuple et pas une liste de dico. On peut en plus lui passer un paramêtre flat que l’on peut mettre à true , pour ‘aplatir’ les tuples quand l’on demande qu’un seul champ (juste coolos quand on veut une liste de pk)

>>> Entry.objects.values_list('id').order_by('id')
[(1,), (2,), (3,), ...]

>>> Entry.objects.values_list('id', flat=True).order_by('id')
[1, 2, 3, ...]

A quoi sert values et values_list ?

L’intérêt c’est qu’un ValuesQuerySet, c’est comme un queryset. Et que donc on peut utiliser toutes les méthodes des queryset dessus. Y compris refiltrer, order_by, etc etc ..

Et que dans le même temps, on peut alléger la charge, surtout si on a des gros models dont l’on ne veut utiliser que quelques champs.

defer (*fields)

Permet d’indiquer au queryset de ne pas récupérer automatiquement le contenu des champs qui sont passés en paramêtre du defer. Ca peut être utile dans le cas de gros champ texte par exemple (imaginons une vue en liste de billet de blog où l’on ne veut que les titres des billets et pas leur contenu, utiliser un defer(‘body’) pourrait être une possibilité , utiliser un values en serait une autre)

les champs deferred seront récupérés quand on les appelera explicitement. Pour annuler les defer d’un queryset, il suffit d’appeler la fonction avec None en paramêtre.

only (*fields)

C’est l’inverse du defer, on ne récupère que certains champs.

2- Les fonctions qui ne renvoient pas un queryset.

in_bulk(id_list)

Cette petite fonction bien sympa prend une liste de pk et renvoie un dico des objets qui correspondent (les clés étant les pk)

latest(field_name=None)

Renvoie le dernier objet, inséré dans la base, en se basant sur les dates et en utilisant le champ passé en paramètre (qui doit donc être un champ  date).
Si le model en question définit la Meta get_latest_by, on peut appeler latest sans argument.

Ok, cette fonction ‘ne sert à rien’ à part pour rendre plus lisible le code. Mais bon, ça ne mange pas de pain. Et l’utilisation de la Meta get_latest_by permet de ‘centraliser’ la façon de rechercher le dernier, ce qui rendra une modification plus facile.

aggregate(*args, **kwargs)

Retourne un dico des valeurs d’aggrégat calculés non pas objet par objet comme avec annotate, mais sur tout le queryset.

A vous les sommes de champ, les moyennes ou autre. Vive les rapports :).

>>> q = Blog.objects.aggregate(Count('entry'))
{'entry__count': 16}
>>> q = Blog.objects.aggregate(number_of_entries=Count('entry'))
{'number_of_entries': 16}

et comme pour annotate, on peut contrôler le nom, ici de la clé, que notre valeur calculée.

exists()

cette fonction n’existe pas encore dans django, elle sera présente dans la 1.2. Elle permet tout simplement de savoir si le queryset est vide ou pas. (oui je sais, devoir attendre la 1.2 pour avoir cette fonction.. mais bon 🙂 ).

  One Response to “Petits tours des méthodes des querysets.”

  1. […] This post was mentioned on Twitter by Jean-Michel ARMAND, Florian Strzelecki. Florian Strzelecki said: RT @MrJMad: [BLOG] petit billet décrivant quelques fonctions des querysets #django : http://2tu.us/1wsy #djangofr […]

Sorry, the comment form is closed at this time.