Django Queryset

Queryset c'est quoi?

Lorsque vous avez crée un modèle, vous pouvez utiliser un manager de ce modèle avec la syntaxe suivante:

Product.objects

Ce manager génère des queryset qui sont des points d'entrée vers l'ORM de Django. C'est grâce à ce manager que vous allez pouvoir communiquer avec votre base de données. Un queryset est donc une liste d'objets d'un modèle donné.

Voic un exemple de queryset:

Product.objects.all()

Ce queryset vous retournera une liste avec toutes les entrées en base du modèle Product.

Il existe d'autres possibilités de recherche:

# Retourne tous les produits où la date est supérieure à 2010
Product.objects.filter(date__gt=2010)

# Retourne les produits à 15 euros
Product.objects.filter(price_ttc=15)

# Retourner les produits qui ne sont pas à 15 euros
Product.objects.exclude(price_ttc=15)

Il est d'ailleurs possible de chainer les filtres pour affiner sa recherche:

Product.objects.filter(date__lt=2010).exclude(price_ttc=15).order_by(name)

Les relations dans les queryset

Rapidement vous lierez les objets entre eux. Pour les retrouver vous pouvez partir de l'objet que vous voulez:

product_item = ProductItem.objects.filter(product__id=1)

Revient au meme que:

product_item = Product.objects.get(id=1).productitem_set.all()

Modifier des données

Exemple de modification:

p = Product.objects.get(pk=1)
p.name = "ipod"
p.save()

Ou cette syntaxe

Product.object.get(pk=1).update(name="ipod")

Supprimer des données

Pour supprimer des entrées dans votre base, vous pouvez utiliser la méthode delete:

Product.objects.find(id=1).delete()

Faire des modifications groupées

Si vous voulez faire une modification de données sur plusieurs items vous pouvez utiliser la méthode update:

Product.objects.filter(date__year=2014).update(name="ipad")

Les méthodes qui retournent un queryset

filter(**kwargs) filtre
exclude(**kwargs) exclu
annotate(*args, **kwargs) permet d'associer des valeurs type moyenne, sum et count au queryset
order_by(*fields) trie par le colonne désignée
reverse() inverse l'ordre du queryset
distinct([*fields]) élimine les doublons
values(*fields) retourne un dictionnaire au lieu d'instances de modèles
values_list(*fields) retourne un tuple au lieu d'instance de modèles
dates(field, kind, order='ASC') transforme les données dans un format datetime.date
datetimes(field, kind, order='ASC', tzinfo=None) transforme les données dans un format datetime.datetime
none() crée un queryset qui ne retourne jamais d'objets
all() l'inverse de none, c'est à dire tout
select_related(*fields) retourne un queryset qui "suit" la clé étrangère indiqué
prefetch_related(*lookups) optimise les requêtes pour éviter un déluge de SELECT
extra(select=None, where=None, params=None, tables=None, order_by=None, select_params=None)hook requete SQL pour créer des nouveaux champs
defer(*fields)¶ ignore des colonnes dans la requete SQL
only(*fields)¶ sélectionne uniquement les champs indiqués pour la reqête SQL
using(alias) indique la base de données à exploiter
select_for_update(nowait=False) retourne un queryset qui bloquera les lignes jusqu'à la fin de la transaction, générant un SELECT ... FOR UPDATE SQL statement sur les bases de données qui le supporte.
raw(raw_query, params=None, translations=None) Execute une requete SQL et retourne un modèle

Les méthodes qui ne retournent pas un queryset

get(**kwargs) retourne un objet suivant les paramètres indiqués
create(**kwargs) créer un objet
get_or_create(defaults=None, **kwargs) récupère un objet ou le crée
update_or_create(defaults=None, **kwargs) modifie un objet ou le crée
bulk_create(objs, batch_size=None) insert en une seule fois une liste d'objets dans la base
count() compte le nombre d'occurences
in_bulk(id_list) prend une liste de clé primaire et retourne les objets associés
iterator() retourne un itérateur
latest(field_name=None) retourne le dernier objet dans la table
earliest(field_name=None) retourne l'objet dans la base le plus récent
first() retourne le premier objet du queryset
last() retourne le dernier objet du queryset
aggregate(*args, **kwargs) retourne un dictionnaire des valeurs regroupées telles que sum, count, average, etc.
exists() retourne True si le queryset contient un résultat
update(**kwargs) modifier la valeur d'un champ
delete() Execute une requete SQL delete sur toutes les lignes dans le queryset
as_manager() retourne le manager

Les champs de recherche

exactExacte correspondance. Si un None est demandé, la recherche sera Null au niveau SQL
iexactinsensible à la casse (qu'importe les majuscules ou les minuscules). ILIKE "XXX" est utilisé au niveau SQL
containscontient tel élément. LIKE '%XXX%'
icontainscontient tel élément quelque soit la casse ILIKE '%XXX%'
inest dans la liste indiqué. exemple .filter(id__in=[1, 2, 3]) en SQL: WHERE id IN (1, 2, 3)
gtplus grand que exemple: filter(id__gt=4) en sql: WHERE id > 4
gteplus grand ou égal
ltplus petit que
lteplus petit ou égal
startswithCommence par. en sql LIKE 'XXX%'
istartswithinsensible à la casse. en slq ILIKE 'XXX%'
endswithse termine par. en SQL LIKE '%XXX'
iendswithinsensible à la casse. en SQL: ILIKE '%XXX'
rangeintervalle de date. exemple: filter(date_creation=(start_date, end_date))
yearrecherche par année. exemple: filter(date_creation__year=2014)
monthrecherche par mois. exemple: filter(date_creation__month=10)
dayrecherche par jour. exemple: filter(date_creation__day=10)
week_dayrecherche par jour dans la semaine
hourrecherche par heure
minuterecherche par minute
secondrecherche par seconde
isnullrecherche par valeur Null. exemple: filter(date_creation__isnull=True)
searchrecheche fonctionnant que sur MySQL utilisant la recherche full-text: filter(name__search="+Tshirt -product Short")
regexrecherche avec une expression régulière. exemple: Product.objects.get(name__regex=r'^(PR?|TR) +')
iregexidem avec insensibilité à la casse

Les fonctions de regroupement / calcul

Avg       → moyenne
Count     → nombre occurences
Max       → maximum
Min       → minimum
StdDev    → écart-type
Sum       → somme
Variance  → variance

Exemples d'utilisation

la méthode annotate

>>> from django.db.models import Count

>>> p = Product.objects.annotate(nb_items=Count("product_item")).get(pk=1)
>>> p.nb_items
2

les méthodes values et values_list

>>> Product.objects.filter(pk=1)
[Product object]
>>> Product.objects.filter(pk=1).values()
[{'code': u'a0', 'name': u'Tshirt', 'price_ht': Decimal('25'), 'date_creation': datetime.datetime(2010, 9, 7, 12, 55, 55, 935974, tzinfo=), 'price_ttc': Decimal('30'), u'id': 1}]
>>> Product.objects.filter(pk=1).values_list()
[(1, u'Tshirt', u'a0', Decimal('25'), Decimal('30'), 0, datetime.datetime(2014, 9, 7, 12, 55, 55, 935974, tzinfo=))]
>>> Product.objects.filter(pk=1).values("code")
[{'code': u'a0'}]
>>> Product.objects.filter(pk=1).values_list("code")
[(u'a0',)]

La méthode extra

>>> p = Product.objects.extra(select={'items_count': 'SELECT COUNT(*) FROM backoffice_productitem WHERE backoffice_productitem.product_id = backoffice_product.id'})
>>> p[0].items_count
2

Voir la requete SQL

Vous pouvez voir quelle sera la requete SQL utilisé par le manager avec l'attribut query

Product.object.all().query

Retournera:

SELECT "backoffice_product"."id", "backoffice_product"."name", "backoffice_product"."code", "backoffice_product"."price_ht", "backoffice_product"."price_ttc" FROM "backoffice_product"

L'objet Q

Les requètes de base proposées par l'ORM Django reste simple et donc limité. Lorsque vous chainez les méthodes, l'opérateur "AND" sera utilisé, si vous voulez utiliser l'opérateur "OR", il vous faudra utiliser l'objet Q

Pour importer le module:

from django.db.model import Q

Exemple d'utilisation:

Product.objects.filter(Q(id=1))

Avec l'opérateur AND:

Product.objects.filter(Q(id=1) & Q(name="TEST"))

Et avec l'opérateur OR:

Product.objects.filter(Q(id=1) | Q(id=2))

Utiliser le notion de contraire:

Product.objects.filter(~Q(id=1))

Pensez à utiliser l'attribut query pour vérifier vos requètes SQL en cas de doute:

print Product.objects.filter(~Q(id=1))
# SELECT "backoffice_product"."id", "backoffice_product"."name", "backoffice_product"."code", "backoffice_product"."price_ht", "backoffice_product"."price_ttc" FROM "backoffice_product" WHERE NOT ("backoffice_product"."id" = 1 )

L'objet F

L'objet F vous permet d'incrémenter des valeurs d'une colonne:

Product.objects.filter(code="0001").update(stock=F("stock")+1)