Django-fr | Documentation | Référence de l'API de la base de données

Django-fr

Documentation Django

Référence de l'API de la base de données

Après avoir créé vos modèles de données (data models), Django met automatiquement à votre disposition une API d'abstraction de la base de données qui vous permet de créer, récupérer, mettre à jour ou effacer des objets. Ce document explique comment.

Dans l'ensemble de ce document, nous ferons référence aux modèles suivants, qui représentent une application de blog:

class Blog(models.Model):
    nom = models.CharField(max_length=100)
    titre = models.TextField()

    def __unicode__(self):
        return self.nom

class Auteur(models.Model):
    nom = models.CharField(max_length=50)
    email = models.EmailField()

    def __unicode__(self):
        return self.nom

class Billet(models.Model):
    blog = models.ForeignKey(Blog)
    titre = models.CharField(max_length=255)
    texte = models.TextField()
    date_publication = models.DateTimeField()
    auteurs = models.ManyToManyField(Author)

    def __unicode__(self):
        return self.titre

Création d'objets

Pour représenter les données d'une table de la base de données en objets Python, Django utilise un système intuitif: la classe d'un modèle représente une table de la base de données, et une instance de cette classe correspond à un enregistrement précis dans cette table.

Pour créer un objet, instanciez le en utilisant comme paramètres les noms des champs définis dans la classe modèle, puis appelez save() pour l'enregistrer dans la base de données.

On importe la classe du modèle d'où qu'il soit dans le path Python, comme vous pouvez l'imaginez. (Cette remarque parce que dans les versions précédentes de Django, l'importation de modèles était plus complexe.)

En supposant que les modèles se trouvent dans mysite/blog/models.py, voilà un exemple:

from mysite.blog.models import Blog
b = Blog(nom='Le blog des Beatles', titre='Toutes les dernières nouvelles des Beatles.')
b.save()

Cela crée, en coulisse, une instruction SQL INSERT. Django ne l'exécute pas tant que vous n'appelez pas explicitement save().

La méthode save() n'a pas de valeur de retour.

Pour créer et sauver un objet en une seule fois, voir la méthode create.

Incrémentation automatique des clefs primaires

Si un modèle a un champ AutoField (une clef primaire qui s'incrémente automatiquement) cette valeur incrémentée automatiquement sera calculée et enregistrée comme une propriété de votre objet la première fois que vous appellerez save().

Exemple:

b2 = Blog(nom='Parlons du Camembert', titre='Réflexions sur les fromages.')
b2.id     # Renvoie None, car b n'a pas encore d'ID.
b2.save()
b2.id     # Renvoie l'ID de notre nouvel objet.

Il n'y a aucun moyen de connaître la valeur d'un champ ID avant d'avoir appelé save(), parce que cette valeur est calculée par la base de données, pas par Django.

(Pour des raisons pratiques, tous les modèles ont, par défaut, un champ AutoField nommé id à moins qu'on ne spécifie explicitement primary_key=True sur un autre champ. voir AutoField documentation)

Définir explicitement les valeurs d'un champ de clef primaire automatique

Si un modèle a un champ AutoField mais que vous souhaitez définir un autre ID pour cet objet, attribuez le explicitement avant de sauver l'objet, plutôt que de compter sur l'attribution automatique de l'ID.

Exemple:

b3 = Blog(id=3, nom='Parlons du Camembert', titre='Réflexions sur les fromages.')
b3.id     # Renvoie 3.
b3.save()
b3.id     # Renvoie 3.

Si vous affectez manuellement la valeur d'un champ de clef primaire automatique, faites attention de ne pas utiliser une valeur déjà existante. Si vous utilisez une valeur qui se trouve déjà dans la base de données, Django agira comme si vous mettiez à jour l'objet existant au lieu d'en créer un nouveau.

En considérant l'exemple précédent ('Parlons du Camembert'), le code ci-dessous remplacera l'entrée dans la base de donnée:

b4 = Blog(id=3, nom='Parlons du Roquefort', titre='Tout sauf du fromage.')
b4.save()  # Ecrase le blog précédent avec ID=3!

Voir Comment Django sait s'il doit faire un UPDATE ou un INSERT, ci-dessous, pour comprendre ce qui se passe.

Spécifier explicitement la valeur d'une clef primaire est surtout utile pour enregistrer de gros volumes d'objets, quand on est certain qu'il n'y aura pas de conflit au niveau des clefs primaires.

Que se passe-t-il lorsque vous effectuer un enregistrement ?

Lorsque que l'on enregistre un objet, Django effectue les étapes suivantes:

  1. Émission d'un signal de pré-enregistrement (``pre_save``). Indique que l'objet va bientôt être enregistré. On peut mentionner un listener qui sera appelé à chaque émission d'un signal. (Il n'existe pas encore de documents sur ces signaux.)

  2. Prétraitement des données. Chaque champ de l'objet reçoit l'ordre d'effectuer une modification automatique des données si nécessaire.

    La plupart des champs ne font aucun prétraitement. Le champ de données est conservé tel quel. Le prétraitement est réservé aux champs ayant un comportement spécifique. Par exemple, si votre modèle à un champ DateField avec l'attribut auto_now=True, la phase de préparation à la sauvegarde modifiera les données de l'objet pour s'assurer que le champ de date contient la date du jour. (Notre documentation n'inclut pas encore une liste de tous les champs ayant un "comportement spécifique".)

  3. Préparation des données pour la base de données. Chaque champ reçoit l'ordre de fournir sa valeur actuelle dans un type de données qui puisse être écrit dans la base de données.

    La plupart des champs ne nécessitent aucune préparation. Les types de données simples, comme les entiers ou les chaînes de caractères, sont 'prêts pour l'écriture' en tant qu'objet Python. Cependant, les types de données plus complexes nécessitent souvent quelques modifications.

    Par exemple, les champs DateField utilisent un objet Python datetime pour stocker les données. Les bases de données ne pouvant pas stocker des objets datetime, la valeur du champ doit être convertie en une chaîne de caractères conforme à l'ISO pour pouvoir être insérée dans la base de données.

  4. Insertion des données dans la base de données. Les données prétraitées et préparées sont ensuite transformées en une requête SQL d'insertion dans la base de données.

  5. Émission d'un signal de post-enregistrement (``post_save``). Tout comme le signal de pré-enregistrement, il sert à indiquer que l'objet a été enregistré correctement.

Enregistrer les modifications des objets

Pour enregistrer les changements d'un objet qui existe déjà dans la base de données, utilisez save().

Soit une instance b5 de la classe Blog qui existe déjà dans la base de données, dans cet exemple on change son nom et on met à jour l'objet dans la base de données:

b5.nom = 'Nouveau nom'
b5.save()

Cela crée, en coulisse, une instruction SQL UPDATE. Django ne l'exécute pas tant que vous n'appelez pas explicitement save().

La méthode save() n'a pas de valeur de retour.

Enregistrement des champs ForeignKey et ManyToManyField

La mise à jour d'un champ ForeignKey fonctionne exactement de la même manière que l'enregistrement d'un champ simple. Il suffit d'associer un objet du bon type au champ en question:

blog_fromage = Blog.objects.get(nom="Parlons du Camembert")
billet.blog = blog_fromage
billet.save()

La mise à jour d'un champ ManyToManyField fonctionne un peu différemment. On utilise la méthode add() sur le champ pour ajouter un enregistrement à la relation:

joe = Author.objects.create(nom="Joe")
Billet.authors.add(joe)

Django fera la grimace si vous essayez d'affecter ou d'ajouter un objet du mauvais type.

Comment Django sait s'il doit faire un UPDATE ou un INSERT

Vous avez peut-être remarqué qu'on utilise la méthode save() que ce soit pour créer ou pour modifier des objets. Avec Django, il n'est pas nécessaire d'utiliser une instruction SQL INSERT ou UPDATE. Pour être précis, lorsqu'on appelle la méthode save(), il utilise l'algorithme suivant:

  • Si la clef primaire de l'objet possède une valeur que l'on peut estimer à True (c'est-à-dire qui ne soit ni None ni une chaîne vide), Django exécute une requête SELECT pour vérifier s'il existe un enregistrement avec la même clef primaire.
  • S'il en trouve un, Django exécute alors un UPDATE pour le mettre à jour.
  • Si la clef primaire de l'objet n'est pas spécifiée, ou qu'elle l'est mais qu'elle n'existe pas dans la table, Django exécute un INSERT.

Le point à retenir est qu'il ne faut pas définir explicitement une valeur à la clef primaire quand on enregistre de nouveaux objets si on n'est pas certain que cette valeur n'est pas déjà utilisée. Pour plus de détails à ce propos, reportez-vous au paragraphe "Définir explicitement la valeur d'un champ de clef primaire automatique" ci-dessus.

Récupération des objets

Pour récupérer des objets de votre base de données, il vous faut construire un QuerySet via un Manager sur la classe de votre modèle.

Un QuerySet représente un groupe d'objets de votre base de données. Il peut être constitué d'aucun, d'un seul ou de plusieurs filtres qui affinent la sélection d'objets selon les paramètres transmis. En SQL, un QuerySet équivaut à un SELECT, et un filtre à une clause comme WHERE ou LIMIT.

On obtient un QuerySet en utilisant le Manager du modèle. Chaque modèle a au moins un Manager nommé, par défaut, objects. On y accède directement via la classe du modèle comme suit:

Blog.objects  # <django.db.models.manager.Manager object at ...>
b = Blog(nom='Foo', titre='Bar')
b.objects     # AttributeError: "Manager isn't accessible via Blog instances."

(Les Managers ne sont accessibles que par la classe du modèle (méthode de classe), et non pas par une de ses instances. Et cela pour bien signifier la séparation entre les opérations au niveau table et les opérations au niveau enregistrement)

Le Manager est la source principale de QuerySets d'un modèle. Il fonctionne comme un QuerySet racine qui décrit tous les objets d'un modèle qui sont dans la base de données. Par exemple, Blog.objects est le QuerySet de base qui contient tous les objets Blog qui sont dans la base de données.

Récupération tous les objets

La manière la plus simple de récupérer les objets d'une table est de les prendre tous. Pour ce faire on utilise la méthode all() sur un Manager.

Exemple:

tout_les_billets = Billet.objects.all()

La méthode all() renvoie un QuerySet de tous les objets de la base.

(Si Billet.objects est un QuerySet, pourquoi ne peut-on pas simplement utiliser Billet.objects ? Parce que Billet.objects est le QuerySet racine; c'est un cas particulier, il ne peut pas être évalué. La méthode all(), elle, renvoie un QuerySet qui peut être évalué.)

Filtrage des objets

Le QuerySet racine fourni par le Manager décrit tous les objets de la base. Mais en général on a plutôt besoin d'accéder à une sélection particulière d'objets plutôt qu'à tous.

Pour créer une sélection, on affine le QuerySet de base en lui ajoutant des filtres. Les deux façons les plus simples sont les suivantes:

filter(**kwargs)
Renvoie un nouveau QuerySet contenant les objets qui correspondent aux critères de recherche fournis.
exclude(**kwargs)
Renvoie un nouveau QuerySet contenant les objets qui ne correspondent pas aux critères de recherche.

Les paramètres de recherche, (**kwargs dans l'exemple précédent) doivent être dans le format Recherche sur un champ décrit ci-dessous.

Par exemple pour obtenir un QuerySet de tous les billets de l'année 2006, on utilise filter() de cette façon:

Billet.objects.filter(date_publication__year=2006)

(Notez que l'on a pas à utiliser all() -- Billet.objects.all().filter(...). Cela fonctionnerait quand même, mais c'est inutile. On utilise all() seulement quand on veut tous les objets.)

Chaînage des filtres

Le résultat de l'affinage d'un QuerySet est un autre QuerySet. Il est donc possible d'enchaîner les filtres. Par exemple:

Billet.objects.filter(
    titre__startswith='Quel').exclude(
        date_publication__gte=datetime.now()).filter(
            date_publication__gte=datetime(2005, 1, 1))

...utilise le QuerySet de base de tous les enregistrements de la table, y ajoute un filtre, puis une exclusion et enfin un autre filtre. Le résultat est un QuerySet contenant tous les billets dont le titre commence par "Quel", publiés entre le 1er janvier 2005 et aujourd'hui.

Chaque QuerySet filtré est unique

A chaque fois qu'on affine un QuerySet, on obtient un QuerySet tout neuf aucunement lié au précédent. Chaque affinage crée donc un QuerySet bien distinct, qui peut être stocké, utilisé et réutilisé.

Exemple:

q1 = Billet.objects.filter(titre__startswith="Quel")
q2 = q1.exclude(date_publication__gte=datetime.now())
q3 = q1.filter(date_publication__gte=datetime.now())

Ces trois QuerySets sont distincts. Le premier est un QuerySet de base contenant tous les billets dont le titre commence par "Quel". Le second est un sous groupe du premier. Le critère supplémentaire exclut les billets dont le champ date_publication est supérieur à la date d'aujourd'hui. Le troisième est lui aussi un sous groupe du premier, qui contient uniquement les billets dont le champ date_publication est supérieur à la date d'aujourd'hui. Le QuerySet de base (q1) n'est aucunement affecté par ce processus de filtrage.

Les QuerySets sont passifs

Les QuerySets sont passifs, parce que leur création n'implique aucun appel à la base de données. On peut empiler des filtres toute la journée, Django n'exécutera vraiment la requête que lorsque le QuerySet sera évalué.

Quand les QuerySets sont-ils évalués ?

On peut évaluer un QuerySet par les façons suivants:

  • Itération. Un QuerySet est un itérateur, et il exécute sa requête sur la base de données la première fois qu'on lance une itération dessus. Par exemple, pour afficher les titres de toutes les billets de la base de données, on aura:

    for b in Billet.objects.all():
        print b.titre
    
  • Découpage. Comme on l'explique ci-dessous dans le paragraphe Limiter un QuerySet, un QuerySet peut être découpé en utilisant la syntaxe Python pour le découpage de liste. En général découper un QuerySet renvoie un autre QuerySet (non évalué), mais Django exécutera une requête sur la base de données si vous utilisez le paramètre "pas" de la syntaxe de découpage.

  • repr(). Un QuerySet est évalué quand on le passe à la fonction repr(). Ceci est pratique quand on utilise l'API dans l'interpréteur interactif car on voit tout de suite les résultats.

  • len(). Un QuerySet est évalué quand on le passe à la fonction len(). Comme on peut s'en douter, cela renvoie la longueur du QuerySet.

    Note: N'utilisez pas len() si vous voulez juste savoir le nombre d'éléments contenus dans un QuerySet. Il est beaucoup plus efficace de demander à la base de données de les compter, en utilisant une instruction SQL SELECT COUNT(*). Django propose précisément une méthode count() pour ça. Voir count() plus bas.

  • list(). On peut forcer l'évaluation d'un QuerySet en utilisant la fonction list(). Par exemple:

    liste_billet = list(Billet.objects.all())
    

    Attention quand même car cela peut entraîner une très grosse consommation de mémoire parce que Django chargera chaque élément de la liste en mémoire. A l'opposé, lorsque que l'on parcourt un QuerySet par itération, on profite de la capacité des bases de données à charger les données et générer les objets au fur et à mesure qu'on en a besoin.

Sérialisation de QuerySet

Si on sérialise un QuerySet, cela forcera le chargement de tous les résultats en mémoire avant la sérialisation. La sérialisation est généralement utilisée en tant que précurseur pour la sauvegarde et lorsque le QuerySet sauvé est restauré, on désire que les résultats soient aussi présents. Cela signifie que lorsque l'on désérialise un QuerySet, cela contient les résultats au moment de la sérialisation, et non les résultats actuels de la base de données.

Si l'on veut uniquement sérialiser les informations nécessaires pour récréer le QuerySet ultérieurement à partir de la base de données, il suffit de sérialiser l'attribut query du QuerySet. On peut alors récréer le QuerySet original (sans aucun résultat chargé) en utilisant un code comme celui-ci:

>>> import pickle
>>> requete = pickle.loads(s)     # Supposons que 's' est la chaîne de sérialisation.
>>> qs = MonModele.objects.all()
>>> qs.query = requete            # Restaure la requête original.

Limiter un QuerySet

On utilise la syntaxe de découpage de liste de Python pour limiter un QuerySet à un certain nombre de résultats. C'est l'équivalent des clauses LIMIT et OFFSET en SQL.

Par exemple, pour renvoyer les 5 premiers objets (LIMIT 5), on aura:

Billet.objects.all()[:5]

Pour renvoyer cinq objets, du sixième au dixième (OFFSET 5 LIMIT 5), on aura:

Billet.objects.all()[5:10]

On peut aussi découper à partir de l'élément 'N' jusqu'à la fin du QuerySet. Par exemple, pour renvoyer tous les éléments à partir du sixième, on aura:

Billet.objects.all()[5:]

L'implémentation de l'exemple précédent en SQL varie selon la base de données utilisée, mais dans tous les cas, cette syntaxe est supportée.

En général, le découpage d'un QuerySet renvoie un nouveau QuerySet sans évaluation de la requête. Ça n'est pas le cas lorsqu'on utilise le paramètre "step" de la syntaxe de découpage de Python. Dans l'exemple suivant, la requête sera exécutée pour obtenir un objet sur deux dans l'intervalle allant du 1er au 10ème:

Billet.objects.all()[:10:2]

Pour récupérer un seul objet, plutôt qu'une liste (SELECT foo FROM bar LIMIT 1), utilisez un simple index plutôt qu'un intervalle. L'exemple ci-dessous renvoie le premier objet, après les avoir mis dans l'ordre alphabétique en fonction de leur titre:

Billet.objects.order_by('titre')[0]

Le résultat sera à peu près le même avec l'instruction suivante:

Billet.objects.order_by('titre')[0:1].get()

A noter cependant que la première syntaxe lèvera une exception IndexError si aucun objet ne correspond aux critères, alors que la seconde lèvera une exception DoesNotExist.

Les méthodes des QuerySets qui renvoient d'autres QuerySets

Django fournit une série de méthodes d'affinage des QuerySets, qui modifient soit le type de résultat renvoyé soit la manière dont la requête SQL est exécutée.

filter(**kwargs)

Renvoie un nouveau QuerySet contenant les objets qui correspondent aux critères de recherche.

Les paramètres de recherche (**kwargs) doivent être dans le format décrit dans la rubrique Recherche sur un champ plus bas. Si il y a plusieurs paramètres, ils sont ajoutés en utilisant AND dans l'instruction SQL sous-jacente.

exclude(**kwargs)

Renvoie un nouveau QuerySet contenant tous les objets qui ne correspondent pas aux critères de recherche.

Les paramètres de recherche (**kwargs) doivent être dans le format décrit dans la rubrique Recherche sur un champ. Si il y a plusieurs paramètres, ils sont joints dans la requête SQL par un AND et contenus dans un NOT().

Dans cet exemple, tous les enregistrements dont date_publication est postérieur au 3/1/2005 ET ceux dont le champ titre est "bonjour":

Billet.objects.exclude(date_publication__gt=datetime.date(2005, 1, 3), titre='bonjour')

La requête SQL correspondante est:

SELECT ...
WHERE NOT (date_publication > '2005-1-3' AND titre = 'Bonjour')

Dans ce deuxième exemple, tous les enregistrements dont date_publication est postérieur au 3/1/2005 OU ceux dont le champ titre est "bonjour":

Billet.objects.exclude(date_publication__gt=datetime.date(2005, 1, 3)).exclude(titre='bonjour')

La requête SQL correspondante est:

SELECT ...
WHERE NOT date_publication > '2005-1-3'
AND NOT titre = 'bonjour'

Ce dernier exemple est donc plus restrictif.

order_by(*fields)

Par défaut, les résultats renvoyés par un QuerySet sont classés d'après le tuple de classement du paramètre ordering de la classe Meta du modèle. On peut redéfinir cet ordre au niveau de chaque QuerySet en utilisant sa méthode order_by.

Exemple:

Billet.objects.filter(date_publication__year=2005).order_by('-date_publication', 'titre')

Le résultat de ce QuerySet sera classé en ordre décroissant du champ date_publication, puis en ordre croissant du champ titre. Le signe "moins" dans "-date_publication" indique l'ordre décroissant. L'ordre croissant est implicite. Pour obtenir un ordre aléatoire, utilisez "?":

Billet.objects.order_by('?')

Note: Les requêtes order_by('?') peuvent être coûteuses et lentes selon la base de données utilisée.

Pour classer sur un champ se trouvant dans une autre table, on utilise la même syntaxe qu'avec les requêtes sur les relations des modèles. C'est-à-dire, le nom du champ, suivi par double soulignement (__), suivi par le nom du champ dans le nouveau modèle, et ainsi de suite jusqu'au modèle désiré. Par exemple:

Billet.objects.order_by('blog__nom', 'titre')

Si on essaye de classer par un champ qui est une relation vers un autre modèle, Django utilisera l'ordre par défaut défini dans le modèle relié (ou trier par la clef primaire du modèle relié s'il n'y a pas de Meta.ordering spécifié). Par exemple:

Billet.objects.order_by('blog')

...est identique à:

Billet.objects.order_by('blog__id')

...puisque le modèle Blog n'a pas d'ordre prédéfini.

Il faut faire attention à l'utilisation du tri par un champ sur un objet relié lorsqu'on utilise aussi distinct(). Voir la note dans la section distinct() pour comprendre comment le tri par objet relié peut modifié les résultats attendus.

Il est toléré de spécifier un champ multi-valué pour trier les résultats (par exemple, un champ ManyToMany). Normalement, cela n'est pas une chose sensé et cela est vraiment une fonctionnalité pour un usage avancé. Cependant, si vous savez que les données filtrées de votre QuerySet ou les données disponibles implique qu'il n'y aura qu'un tri possible pour chaque élément sélectionné, ce tri peut-être exactement ce que vous cherchiez. Utilisez le tri sur champ multi-valué avec précaution et vérifié que les résultats obtenus sont ceux désirés.

Nouveau dans la version de développement: Si on ne souhaite qu'aucun ordre ne soit appliqué à la requête, pas même l'ordre par défaut, on appelle order_by() sans paramètres.

Nouveau dans la version de développement: La syntaxe pour trier grâce aux objets reliés à changer. Voir la documentation Django 0.96 pour l'ancienne syntaxe.

On ne peut pas préciser si le tri doit être sensible à la casse, Django renverra le résultat tel qu'il est renvoyé par votre base de données.

reverse()

Nouveau dans la version de développement

On utilise la méthode reverse() pour inverser l'ordre dans lequel ont été renvoyé les éléments du QuerySet. L'appel à reverse() une seconde fois, restaure l'ordre initial.

Pour retrouver les cinq ''derniers'' éléments d'un QuerySet, on peut utiliser ceci:

mon_queryset.reverse()[:5]

Notez que cela n'est pas exactement la même chose que le découpage à partir de la fin d'une séquence en Python. L'exemple précédent retournera le dernier élément en premier, puis le pénultième et ainsi de suite. S'il s'agissait d'une séquence Python et que nous regardions seq[:-5], le premier élément serait le cinquième en partant de la fin. Django ne supporte pas ce mode d'accès (découpage à partir de la fin), car il est impossible de la faire efficacement en SQL.

distinct()

Renvoie un nouveau QuerySet dont la requête SQL utilise SELECT DISTINCT. Cela permet d'éliminer les doublons.

Par défaut, un QuerySet n'élimine pas les doublons. En pratique ça n'est généralement pas un problème parce que, avec des requêtes simple comme Blog.objects.all() , il n'y a pas possibilité de doublons. Par contre, lorsqu'on fait une requête sur plusieurs tables, cela peut arriver, rendant utile l'usage de la méthode distinct().

Note

Chaque champ utilisé lors d'un appel à ordre_by() est inclut dans la colonne SELECT. Cela peut parfois mener à des résultats inattendus lorsqu'on l'utilise en conjonction avec distinct(). Si vous trié à partir d'un champ sur un objet relié, ces champs seront ajoutés aux colonnes sélectionnées et créeront peut-être des duplicatas. Puisque les colonnes supplémentaires n'apparaissent pas dans le résultat renvoyé (elles ne sont présentes que pour le tri), cela peut parfois renvoyer des résultats non distincts.

Pareillement, si vous utilisez une requête values() pour réduire les colonnes sélectionnées, ces dernières, utilisée dans n'importe quel order_by() (ou l'ordre par défaut du modèle) seront toujours impliqué et affecteront peut-être l'unicité des résultats.

La morale, ici, est que si on utilise distinct(), il faut faire attention à l'ordre par objet relié. Pareillement, lorsque l'on utilise distinct() avec values().

values(*fields)

Renvoie un ValuesQuerySet -- Il s'agit d'un QuerySet représenté sous forme d'une liste de dictionnaires, plutôt que d'une liste d'instances du modèle.

Chacun de ces dictionnaires représente un objet. Chaque clef correspond à une propriété du modèle.

L'exemple suivant illustre la différence de représentation entre les dictionnaires de values() et les listes d'instances habituelles:

# Cette liste contient une instance de l'objet Blog.
>>> Blog.objects.filter(nom__startswith='Beatles')
[Beatles Blog]

# Cette liste contient un dictionnaire.
>>> Blog.objects.filter(nom__startswith='Beatles').values()
[{'id': 1, 'nom': 'Beatles Blog', 'titre': 'Toutes les dernières nouvelles des Beatles.'}]

values() peut recevoir une liste d'arguments, *fields, qui précisent à quels champs le SELECT doit être limité. Si ces champs sont spécifiés, les dictionnaires ne contiendront que les clefs/valeurs de champ pour les champs spécifiés. A défaut, ils contiendront les clefs et les valeurs de tous les champs de la table de la base de données.

Exemple:

>>> Blog.objects.values()
[{'id': 1, 'nom': 'Beatles Blog', 'titre': 'Toutes les dernières nouvelles des Beatles.'}],
>>> Blog.objects.values('id', 'nom')
[{'id': 1, 'nom': 'Beatles Blog'}]

Il est aussi possible de récupérer les valeurs à partir d'une relation ForeignKey en utilisant le double soulignement pour séparer le nom des champs, tout comme pour l'appel à la commande filter(). Par exemple:

>>> Billet.objects.values('blog__nom').distinct()
[{'nom': 'Beatles Blog'}]

Il faut préciser plusieurs subtilités:

  • La méthode values() ne renvoie rien pour les attributs ManyToManyField et lèvera une erreur si l'on essaye de lui passer ce type de champ.

  • Si l'on a un champ appelé foo de type ForeignKey, l'appel par défaut à values() retournera une clé de dictionnaire appelée foo_id, puisque c'est le nom de l'attribut du modèle caché qui enregistre la valeur effective (l'attribut foo renvoie au modèle relié). Lorsqu'on appelle values() avec comme paramètre des noms de champ, on peut aussi passer aussi bien foo ou foo_id pour avoir le même résultat (la clé du dictionnaire correspondera le nom de champ passé en paramètre).

    Par exemple:

    >>> Billet.objects.values()
    [{'blog_id: 1, 'titre': u'Premier billet', ...}, ...]
    
    >>> Billet.objects.values('blog')
    [{'blog': 1}, ...]
    
    >>> Billet.objects.values('blog_id')
    [{'blog_id': 1}, ...]
    
  • Lorsqu'on utilise values() conjointement à distinct(), il faut être conscient que l'ordre des résultats peut être modifié. Voir la note dans la section distinct(), ci-dessus, pour plus de details.

Nouveau dans la version de développement:: Auparavant, il était impossible de passer blog_id à la méthode values() dans l'exemple précédent, était accepté uniquement blog.

Un ValuesQuerySet est utile lorsque l'on sait qu'on ne va avoir besoin que d'un petit nombre des champs disponibles et qu'on n'aura pas besoin de la fonctionnalité globale d'un objet. C'est plus pratique de sélectionner seulement les champs dont on a besoin.

Pour finir, notons qu'un ValuesQuerySet est un descendant de QuerySet et qu'il hérite donc de ses méthodes. On peut utiliser filter() ou order_by()... Oui, ces 2 exemples renvoient bien le même résultat:

Blog.objects.values().order_by('id')
Blog.objects.order_by('id').values()

Les créateurs de Django ont préféré que les méthodes qui affectent le SQL soient devant les méthodes (optionnelles) qui affectent la représentation (comme values()). Mais ça n'a pas d'importance.

values_list(*fields)

Nouveau dans la version de développement

Ceci est une méthode similaire à values() excepté qu'au lieu de retourner une liste de dictionnaires, elle retourne une liste de tuples. Chaque tuple contient la valeur du champ respectif passé en paramètre lors de l'appel à values_list(). Le premier élément est le premier champ, etc. Par exemple:

>>> Billet.objects.values_list('id', 'titre')
[(1, u'Premier billet'), ...]

Si l'on passe en argument uniquement un seul champ, il est possible de passer aussi le paramètre flat. Si celui-ci est vrai (True), les résultats retournés seront des valeurs simples, et non des 1-uplets. Un exemple devrait éclaircir la différence

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

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

C'est une erreur de passer l'argument flat lorsque plusieurs champs sont présents.

Si l'on ne passe aucune valeur à values_list(), cela retournera tous les champs du modèle, dans l'ordre de déclaration.

dates(field, kind, order='ASC')

Renvoie un DateQuerySet -- il s'agit d'un QuerySet d'objets datetime.datime. Il contient autant d'éléments qu'il y a de valeurs distinctes pour le paramètre kind (catégorie) fourni.

Le paramètre field doit être le nom d'un des champs de type DateField ou DateTimeField de votre modèle.

Le paramètre kind doit être une chaîne ayant une des valeurs "year", "month" ou "day". Dans le cas d'un kind "month" ou "day", les dates renvoyées sont tronquées. La précision des dates renvoyées est au niveau du paramètre kind.

  • "year" renvoie une liste de toutes les années distinctes pour ce champ.
  • "month" renvoie une liste de tous les paires mois/année distinctes pour ce champ.
  • "day" renvoie une liste de toutes les dates (jour/mois/année) distinctes pour ce champ

Le paramètre order définit dans quel ordre sont classés les résultats. L'ordre croissant 'ASC' est implicite, sinon on utilise 'DESC'.

Exemples:

>>> Billet.objects.dates('date_publication', 'year')
[datetime.datetime(2005, 1, 1)]
>>> Billet.objects.dates('date_publication', 'month')
[datetime.datetime(2005, 2, 1), datetime.datetime(2005, 3, 1)]
>>> Billet.objects.dates('date_publication', 'day')
[datetime.datetime(2005, 2, 20), datetime.datetime(2005, 3, 20)]
>>> Billet.objects.dates('date_publication', 'day', order='DESC')
[datetime.datetime(2005, 3, 20), datetime.datetime(2005, 2, 20)]
>>> Billet.objects.filter(titre__contains='Lennon').dates('date_publication', 'day')
[datetime.datetime(2005, 3, 20)]

none()

Nouveau dans la version de développement

Renvoie un EmptyQuerySet -- un QuerySet vide. Ca sert lorsque l'on sait qu'on doit renvoyer une liste vide, mais que la fonction appelante attend un résultat de type QuerySet.

Exemples:

>>> Billet.objects.none()
[]

all()

Nouveau dans la version de développement

Renvoie une ''copie'' du QuerySet actuel (ou une classe dérivée du QuerySet passé en paramètre). Cela peut être utile dans certaines situations où l'on désire passer en paramètre soit un manager de modèle, soit un QuerySet et pratiquer d'autres filtrages sur le résultat. On peut appeler sûrement all() sur un objet et on aura alors un QuerySet pour d'autres opérations.

select_related()

Renvoie un QuerySet qui aura automatiquement "suivi" les relations de clefs étrangères (foreign-key). Les objets reliés par ces relations seront mis en cache pour une utilisation ultérieure. Ceci permet d'obtenir des résultats (parfois beaucoup) plus importants en volume, mais le nombre de requêtes nécessaires sera réduit lorsqu'on utilisera les relations de clefs étrangères ultérieurement.

Cet exemple illustre la différence entre une recherche normale et une recherche qui utilise select_related(). Voici une recherche normale:

# Une requête est exécutée
e = Billet.objects.get(id=5)

# Une autre requête est exécutée pour récupérer l'objet relié par
# la relation.
b = e.blog

Avec select_related:

# Une requête est exécutée.
e = Billet.objects.select_related().get(id=5)

# Aucune requête n'est exécutée, l'objet e.blog a été instancié
# et mis en cache lors de la requête précédente.
b = e.blog

select_related() suit autant de relations que possible. Soit le modèle suivant:

class Ville(models.Model):
    # ...

class Personne(models.Model):
    # ...
    ville_naissance = models.ForeignKey(Ville)

class Livre(models.Model):
    # ...
    auteur = models.ForeignKey(Personne)

L'appel de Livre.objects.select_related().get(id=4) mettra en cache l'objet Personne relié à Livre, puis l'objet Ville relié à Personne:

b = Livre.objects.select_related().get(id=4)
p = b.auteur                 # Pas de requête exécutée.
c = p.ville_naissance        # Pas de requête exécutée.

sv = Livre.objects.get(id=4) # On n'utilise pas ``select_related()``
p = b.auteur                 # Une requête est exécutée.
c = p.ville_naissance        # Une requête est exécutée.

Notons que select_related() n'exploite pas les relations de clefs étrangères qui ont null=True.

En général l'emploi de select_related() améliore grandement les performances en réduisant le nombre de requêtes SQL. Par contre, dans le cas où un grand nombre de relations s'enchaînent select_related() peut parfois finir par suivre "beaucoup trop" de relations, et peut générer des requêtes si importantes que cela finit par ralentir l'application.

Dans ce cas, on peut utiliser le paramètre depth pour contrôler combien de niveaux de relations select_related() suivra effectivement:

b = Livre.objects.select_related(depth=1).get(id=4)
p = b.auteur                # Pas de requête exécuté.
c = p.ville_naissance       # Une requête est exécutée.

L'argument depth est nouveau dans la version de développement.

Nouveau dans la version de développement: Parfois on desire accéder qu'à un modèle spécifique relié à notre modèle racine, non à tous. Dans ces cas, on peut passer en argument le nom du champ relié à la méthode select_related() et cela ne suivra que cette relation. On peut aussi faire cela pour les modèles ayant plus d'une relation en séparant les noms des champs avec un double soulignement, comme pour les filtres. Par exemple, si on a le modèle suivant:

class Salle(models.Model):
    # ...
    immeuble = models.ForeignKey(...)

class Classe(models.Model):
    # ...
    professeur = models.ForeignKey(...)
    salle = models.ForeignKey(Salle)
    sujet = models.ForeignKey(...)

...et que l'on désire travailler uniquement les attribut salle et sujet, on peut juste écrire:

g = Classe.objects.select_related('salle', 'sujet')

Ceci est aussi valide:

g = Classe.objects.select_related('salle__immeuble', 'sujet')

...et chargera la relation immeuble.

On peut uniquement se référer à une relation ForeignKey appartenant à la liste des champs passés en argument à select_related. On peut faire référence à des champ ForeignKey ayant null=True (contrairement à l'appel par défaut à select_related()). C'est une erreur d'utiliser à la fois une liste de champ et le paramètre depth dans le même appel à select_related(), puisque ces options rentrent en conflit.

extra(select=None, where=None, params=None, tables=None, order_by=None, select_params=None)

Parfois la syntaxe de requête de Django ne parvient pas exprimer des clauses WHERE complexes. Pour ces cas particuliers, Django fournis le modificateur de QuerySet extra() qui permet d'injecter des clauses spécifiques dans le SQL généré par un QuerySet.

Par définition, ces critères supplémentaires peuvent ne pas être portables d'un moteur de base de données à l'autre, puisqu'il s'agit en fait d'écrire du code SQL. Par ailleurs, cela viole le concept DRY (Don't Repeat Yourself). Il faut donc l'utiliser le moins possible.

extra() prend en paramètre au moins un argument parmi params, select, where ou tables. (ils ne sont pas obligatoires mais il faut en utiliser au moins un).

select

L'argument select permet d'ajouter des champs supplémentaires à la clause SELECT. Cela sera sous la forme d'un dictionnaire, dont la clef est le nom du champ calculé, et la valeur, la formule permettant de le calculer.

Exemple:

Billet.objects.extra(select={'is_recent': "date_publication > '2006-01-01'"})

Le résultat sera donc une liste d'objets Billet ayant une propriété supplémentaire : un booléen nommé is_recent à True si le champ date_publication est une date supérieure au 1er janvier 2006.

Django injecte le petit bout de SQL directement dans l'instruction SELECT. La requête ressemblera à ça:

SELECT blog_billet.*, (date_publication > '2006-01-01')
FROM blog_billet;

L'exemple suivant est plus compliqué; il exécute une sous requête en donnant à chaque objet Blog du résultat de la requête principale, une propriété compteur_billet, qui est le nombre d'objets Billet associés:

Blog.objects.extra(
    select={
        'compteur_billet': 'SELECT COUNT(*) FROM blog_Billet WHERE blog_billet.blog_id = blog_blog.id'
    },
)

(Dans ce cas précis, on exploite le fait que la requête fera déjà référence à la table blog_blog dans sa clause FROM)

La requête SQL générée ressemblera à ça:

SELECT blog_blog.*, (SELECT COUNT(*) FROM blog_Billet WHERE blog_Billet.blog_id = blog_blog.id)
FROM blog_blog;

Notons que les parenthèses nécessaires à la syntaxe des sous requêtes pour la plupart des moteurs de base de données ne le sont pas pour les clauses select de Django. Par ailleurs certains moteurs de base de données ne supportent pas les sous requêtes (certaines anciennes versions de MySQL par exemple).

Nouveau dans la version de développement Dans quelques rares cas, on désire passer des paramètres aux fragments SQL dans extra(select=...). Pour cela, on utilise le paramètre select_params. Puisque select_params est une séquence et l'attribut select un dictionnaire, il faut faire attention à ce que les paramètres correspondent avec les parties supplémentaire sélectionnées. Dans cette situation, il faut utiliser un dictionnaire trié (django.utils.datastructures.SortedDict) pour la valeur sélectionnée, et non pas un dictionnaire normal de Python.

Cela fonctionnera, par exemple:

Blog.objects.extra(
    select=SortedDict(('a', '%s'), ('b', '%s')),
    select_params=('un', 'deux'))
where / tables

On peut ajouter du code SQL à la clause WHERE en utilisant le paramètre where, par exemple pour créer une jointure particulière. On peut ajouter manuellement des tables à la clause FROM en utilisant le paramètre tables.

where et tables acceptent l'un et l'autre une liste de chaînes. Tous les paramètres de where sont ajoutés aux éventuelles autres conditions de la clause WHERE avec un AND.

Exemple:

Billet.objects.extra(where=['id IN (3, 4, 5, 20)'])

...donnera, en gros, en SQL:

SELECT * FROM blog_Billet WHERE id IN (3, 4, 5, 20);

Attention lorsqu'on utilise le paramètre tables, si l'on spécifie des tables déjà utilisées dans la requête. Lorsqu'on ajoute des tables supplémentaires via le paramètre tables, Django suppose que l'on que désire que cette table soit incluse une fois supplémentaire, si elle est déjà incluse. Ceci pose problème, puisque le nom de la table aura un alias. Si une table apparaît plusieurs fois dans une requête SQL, les occurrences suivantes doivent utiliser un alias pour que la base de données puisse les différencier. Si l'on fait référence à la table supplémentaire, que l'on a ajouté dans le paramètre where, cela provoquera des erreurs.

Normalement, on ajoute seulement des tables supplémentaires qui n'apparaissent pas dans la requête. Cependant, si le cas décrit précédemment apparaît, il existe quelques solutions. Premièrement, essayez de ne pas utiliser la table supplémentaire mais celle appartenant déjà à la requête. Si cela n'est pas possible, ajoutez l'appel extra() au début de la construction du QuerySet pour que votre table soit la première utilisation de cette table. Finalement, en dernier recours, regardez la requête produite et réécrivez votre where avec un alias pour la table supplémentaire. Cet alias sera le même à chaque fois que vous construirez un QuerySet de la même façon.

order_by

Si l'on désire classer le QuerySet renvoyé en utilisant des champs ou tables inclus via extra(), on peut utiliser le paramètre order_by à extra() et lui passer une séquence de chaîne de caractères. Ces chaînes de caractères doivent être soit des champs de modèle (comme dans la méthode order_by() normal des QuerySet), soit de la forme nom_table.nom_colonne ou un alias pour la colonne spécifiée grâce au paramètre select dans extra().

Par exemple:

q = Billet.objects.extra(select={'est_recent': "date_publication > '2006-01-01'"})
q = q.extra(order_by = ['-est_recent'])

Cela classera tous les éléments, pour lesquels est_recent est vrai, au début de l'ensemble résultant (True est classé avant False dans l'ordre décroissant).

Cela montre, par ailleurs, que vous pouvez faire plusieurs appels à extra() et que cela se comportera comme vous le souhaitez (ajouter des nouvelles contraintes à chaque fois).

params

Les paramètres select et where décrits ci-dessus peuvent utiliser la syntaxe Python de remplacement de chaîne -- '%s'` -- pour indiquer quels paramètres le moteur de base de données doit mettre entre guillemets. L'argument params est la liste des éventuels paramètres supplémentaires à substituer.

Exemple:

Billet.objects.extra(where=['titre=%s'], params=['Lennon'])

Utilisez toujours params plutôt que d'imbriquer directement les valeurs dans select ou where. Avec params on est sûr que les guillemets utilisés correspondent au moteur de base de données, ou que d'éventuels guillemets seront correctement échappés.

Ne pas faire:

Billet.objects.extra(where=["titre='Lennon'"])

Mais faire:

Billet.objects.extra(where=['titre=%s'], params=['Lennon'])

Nouveau dans la version de développement: L'argument select_params d'extra() est nouveau. Avant, on pouvait essayer de passer des paramètres pour select dans l'argument params, mais cela était très instable.

Les méthodes de QuerySets qui ne renvoient pas de QuerySet

Les méthodes suivantes évaluent le QuerySet duquel elles sont appelées et renvoient autre chose qu'un QuerySet.

Ces méthodes n'utilisent pas le cache (voir plus bas Les QuerySets et leurs caches). Elles exécutent une requête SQL à chaque fois qu'elles sont appelées.

get(**kwargs)

Renvoie l'objet correspondant au paramètre de recherche donné. Ce paramètre doit être fourni au format décrit dans Recherche sur un champ.

get() lève une exception MultipleObjetcsReturned si plusieurs objets sont trouvés. L'exception MultipleObjetcsReturned est un attribut de la classe modèle. Par exemple, ce qui suit lèvera une exception MultipleObjetcsReturned s'il y a plus d'un auteur ayant pour nom 'John':

Auteur.objects.get(nom='John') # lève Auteur.MultipleObjectsReturned

get() lève une exception DoesNotExist si aucun objet n'est trouvé. L'exception DoesNotExist est une propriété de la classe sur modèle. Exemple:

Billet.objects.get(id='foo') # lève l'exception Billet.DoesNotExist

L'exception DoesNotExist hérite de django.core.exceptions.ObjectDoesNotExist, on peut donc intercepter plusieurs exceptions DoesNotExist dans le même bloc except. Exemple:

from django.core.exceptions import ObjectDoesNotExist
try:
    e = Billet.objects.get(id=3)
    b = Blog.objects.get(id=1)
except ObjectDoesNotExist:
    print "L'objet Billet ou l'objet Blog n'existe pas."

create(**kwargs)

Méthode pratique pour la création d'un objet et son enregistrement en une seule ligne. Ainsi:

p = Personne.objects.create(prenom="Bruce", nom="Springsteen")

et:

p = Personne(prenom="Bruce", nom="Springsteen")
p.save()

sont équivalents.

get_or_create(**kwargs)

Méthode pratique pour rechercher un objet selon certains critères de recherche, ou le créer si il n'existe pas.

Cela renvoie un tuple (object, created), ou object est l'objet récupéré ou créé, et created un booléen indiquant si l'objet a été créé ou pas.

Ca permet d'éviter un paragraphe standard de code, et c'est surtout pratique pour les scripts d'importations de données. Par exemple:

try:
    obj = Personne.objects.get(prenom='John', nom='Lennon')
except Personne.DoesNotExist:
    obj = Personne(prenom='John', nom='Lennon', date_naissance=date(1940, 10, 9))
    obj.save()

Grâce à get_or_create(), on peut abréger le code ci-dessus, surtout dans le cas d'un modèle complexe où le nombre de champs augmente:

obj, created = Personne.objects.get_or_create(prenom='John', nom='Lennon',
                  defaults={'date_naissance': date(1940, 10, 9)})

Tous les arguments passés à get_or_create() -- à part defaults qui est optionnel -- seront utilisés lors du get(). Si un objet est trouvé, get_or_create() renvoie un tuple constitué de cet objet et du booléen False. Si aucun objet n'est trouvé, get_or_create() instanciera puis enregistrera un nouvel objet, puis renverra un tuple constitué de ce nouvel objet et du booléen True. Ce nouvel objet sera créé selon l'algorithme suivant:

defaults = kwargs.pop('defaults', {})
params = dict([(k, v) for k, v in kwargs.items() if '__' not in k])
params.update(defaults)
obj = self.model(**params)
obj.save()

En français, ça signifie : qui commence par n'importe lequel des arguments de recherche qui ne soient pas dans 'defaults' et qui ne contient pas un double soulignement (ce qui indiquerait une recherche non-exacte). Puis ajoutez le contenu de defaults, en écrasant une clef si nécessaire, et utilisez le résultat en tant qu'argument mot-clef pour la classe du modèle.

Si vous aviez un champ defaults et vouliez faire une recherche exacte dessus, il faut utiliser 'defaults__exact', comme suit:

Foo.objects.get_or_create(defaults__exact='bar', defaults={'defaults': 'baz'})

Un dernier mot à propos de get_or_create() dans les vues. Comme mentionné plus haut, get_or_create() est surtout utile dans des scripts pour analyser les données. Si vous êtes amené à utiliser get_or_create() dans une vue, il faut vraiment le faire que dans une requête POST à moins d'avoir une bonne raison. Pour plus d'information, voyez Safe methods dans la RFC de HTTP.

count()

Renvoie un entier représentant le nombre d'objets de la base correspondant au QuerySet. count() ne lève jamais d'exception.

Exemple:

# Retourne le nombre total d'objets billet dans la base
Billet.objects.count()

# Retourne le nombre total d'objets billet dont la propriété titre
# contient 'Lennon'
Billet.objects.filter(titre__contains='Lennon').count()

Au niveau SQL, count() exécute un SELECT COUNT(*). Il faut toujours utiliser count() plutôt que d'appeler la fonction len() sur un objet Python contenant tous les objets d'une table.

Selon le moteur de base de données utilisé, (PostgreSQL ou MySQL), count() peut renvoyer un entier long plutôt qu'un entier Python. C'est une petite bizarrerie de l'implémentation sous-jacente qui ne devrait pas poser de vrai problème.

in_bulk(id_list)

Accepte une liste de champs de clef primaire et renvoie un dictionnaire établissant la correspondance entre chaque champ de clef primaire et une instance de l'objet pour l'ID donné.

Exemple:

>>> Blog.objects.in_bulk([1])
{1: Le Blog des Beatles}
>>> Blog.objects.in_bulk([1, 2])
{1: Le Blog des Beatles, 2: Parlons du Camembert}
>>> Blog.objects.in_bulk([])
{}

Si l'on fournit une liste vide à in_bulk(), on obtient un dictionnaire vide.

iterator()

Evalue le QuerySet (en effectuant un requête) et renvoie un itérateur vers les résultats. Un QuerySet lira tous ces résultats et instanciera tous les objets correspondants dès le premier accès. A l'inverse, iterator() lira les résultats et instanciera les objets par paquet, au fur et à mesure. Pour un QuerySet qui retourne un nombre important d'objets, ceci permet d'obtenir de meilleures performances et de réduire considérablement la mémoire utilisée.

Remarquez que l'utilisation d'iterator() sur un QuerySet ayant déjà été évalué forcera une nouvelle évaluation de celui-ci, dupliquant ainsi la requête.

latest(field_name=None)

Renvoie le dernier objet entré dans la table, par rapport à la date, en utilisant comme champ de date le champ field_name fourni.

Dans cet exemple, le dernier Billet de la table est renvoyé, selon le champ date_publication

Billet.objects.latest('date_publication')

Si la classe Meta du modèle fournit l'attribut get_latest_by, on peut laisser vide l'argument field_name de latest(). Django utilisera le champ spécifié dans get_latest_by par défaut.

Tout comme get(), latest() lèvera l'exception DoesNotExist si aucun objet n'existe pour les paramètres donnés.

Notez que latest() existe simplement pour des raisons pratiques et de lisibilité.

Recherche sur un champ

La recherche sur un champ vous permet de spécifier le contenu de la clause WHERE d'une requête SQL. Elle est spécifiée en tant qu'argument mot-clef des méthodes de QuerySet filter(), exclude() et get().

Une recherche de base sur l'argument mot-clef prend la forme champ__typederecherche=valeur. (Il s'agit d'un double soulignement). Par exemple:

Billet.objects.filter(date_publication__lte='2006-01-01')

peut être traduit (grossièrement) par la requête SQL suivante:

SELECT * FROM blog_billet WHERE date_publication <= '2006-01-01';

Comment cela est possible ?

Python a la capacité de définir des fonctions acceptant des arguments nom-valeur arbitraires dont les noms et valeurs seront évalués à l'exécution. Pour plus d'informations, voir Arguments mot-clef dans le tutoriel Python officiel.

Si on passe en argument un mot-clef invalide, la fonction de recherche lèvera l'exception TypeError.

L'API de la base de données accepte les types de recherche suivants:

exact

Correspondance exacte. Si la valeur fournie pour la comparaison est None, elle sera interprétée en tant que SQL NULL (Voir isnull pour plus de détails).

Exemples:

Billet.objects.get(id__exact=14)
Billet.objects.get(id__exact=None)

Equivalents SQL

SELECT ... WHERE id = 14;
SELECT ... WHERE id = IS NULL;

Nouveau dans la version de développement: La sémantique de id__exact=None a changé dans la version de développement. Auparavant, elle était (intentionnellement) convertie en WHERE id = NULL au niveau SQL, ce qui ne correspondait avec aucune données enregistrée. Suite à une modification, elle se comporte maintenant comme id__isnull=True.

iexact

Correspondance exacte insensible à la casse.

Exemple:

Blog.objects.get(nom__iexact='beatles blog')

Equivalent SQL

SELECT ... WHERE nom ILIKE 'beatles blog';

Notez que cela correspondera à 'Beatles Blog', 'beatles blog', 'BeAtLes BLoG', etc.

contains

Test de contenu sensible à la casse.

Exemple:

Billet.objects.get(titre__contains='Lennon')

Equivalent SQL

SELECT ... WHERE titre LIKE '%Lennon%';

Notez que cela correspondera au titre 'On rend hommage à Lennon ' mais pas à 'on rend hommage à lennon'.

SQLite ne supporte pas les déclarations LIKE sensibles à la casse. contains se comportera comme icontains pour SQLite.

icontains

Test de contenu insensible à la casse.

Exemple:

Billet.objects.get(titre__icontains='Lennon')

Equivalent SQL:

SELECT ... WHERE titre ILIKE '%Lennon%';

gt

Supérieur à.

Exemple:

Billet.objects.filter(id__gt=4)

Equivalent SQL:

SELECT ... WHERE id > 4;

gte

Supérieur ou égal à.

lt

Inférieur à.

lte

Inférieur ou égal à.

in

Appartient à la liste donnée.

Exemple:

Billet.objects.filter(id__in=[1, 3, 4])

Equivalent SQL:

SELECT ... WHERE id IN (1, 3, 4);

Il est aussi possible d'utiliser un QuerySet pour évaluer dynamiquement la liste de valeurs au lieu de fournir une liste de valeurs littérales. Le QuerySet doit être réduit en une liste de valeurs individuelles en utilisant la méthode values(), et puis convertit en une requête en utilisant l'attribut query:

Billet.objects.filter(blog__in=Blog.objects.filter(nom__contains='Fromage').values('pk').query)

Ce QuerySet sera évalué en tant que sous-sélection:

SELECT ... WHERE blog.id IN (SELECT id FROM ... WHERE NAME LIKE '%Fromage%')

startswith

Commence par (sensible à la casse).

Exemple:

Billet.objects.filter(titre__startswith='Will')

Equivalent SQL:

SELECT ... WHERE titre LIKE 'Will%';

SQLite ne supporte pas les déclarations LIKE sensibles à la casse. startswith se comportera comme istartswith pour SQLite.

istartswith

Commence par (insensible à la casse).

Exemple:

Billet.objects.filter(titre__istartswith='will')

Equivalent SQL:

SELECT ... WHERE titre ILIKE 'Will%';

endswith

Termine par (sensible à la casse).

Exemple:

Billet.objects.filter(titre__endswith='cats')

Equivalent SQL:

SELECT ... WHERE titre LIKE '%cats';

SQLite ne supporte pas les déclarations LIKE sensibles à la casse. endswith se comportera comme iendswith pour SQLite.

iendswith

Termine par (insensible à la casse).

Exemple:

Billet.objects.filter(titre__iendswith='will')

Equivalent SQL:

SELECT ... WHERE titre ILIKE '%will'

range

Test de plage (inclusif).

Exemple:

date_debut = datetime.date(2005, 1, 1)
date_fin = datetime.date(2005, 3, 31)
Billet.objects.filter(date_publication__range=(date_debut, date_fin))

Equivalent SQL:

SELECT ... WHERE date_publication BETWEEN '2005-01-01' and '2005-03-31';

On peut utiliser range partout où on peut utiliser BETWEEN en SQL (pour les dates, les nombres et même les caractères).

year

Pour les champs de date ou de date/heure, correspondance exacte de l'année. Accepte une année sous la forme de quatre chiffres.

Exemple:

Billet.objects.filter(date_publication__year=2005)

Equivalent SQL:

SELECT ... WHERE EXTRACT('year' FROM date_publication) = '2005';

(La syntaxe SQL exacte est différente pour chaque moteur de base de données.)

month

Pour les champs de date ou de date/heure, correspondance exacte du mois. Accepte un entier de 1 (Janvier) à 12 (Décembre).

Exemple:

Billet.objects.filter(date_publication__month=12)

Equivalent SQL:

SELECT ... WHERE EXTRACT('month' FROM date_publication) = '12';

(La syntaxe SQL exacte est différente pour chaque moteur de base de données.)

day

Pour les champs de date ou de date/heure, correspondance exacte du jour.

Exemple:

Billet.objects.filter(date_publication__day=3)

Equivalent SQL:

SELECT ... WHERE EXTRACT('day' FROM date_publication) = '3';

(La syntaxe SQL exacte est différente pour chaque moteur de base de données.)

Notez que cela correspondera à toute données enregistrée ayant le troisième jour du mois comme date_publication, comme le 3 janvier, le 3 juillet, etc.

isnull

Accepte soit True soit False, qui correspond aux requêtes SQL, respectivement, IS NULL et IS NOT NULL.

Exemple:

Billet.objects.filter(date_publication__isnull=True)

Equivalent SQL:

SELECT ... WHERE date_publication IS NULL;

search

Une recherche booléenne de texte intégral (full-text), ce qui permet de profiter de l'indexation en texte intégral. Cela fonctionne de manière similaire à contains mais beaucoup plus rapidement à cause de l'indexation texte intégral.

Notez que cela n'est disponible que sous MySQL et qu'une manipulation directe de la base de données est nécessaire pour ajouter l'index en texte intégral.

regex

Nouveau dans la version en développement de Django

Correspondance d'expression régulière sensible à la casse.

La syntaxe des expressions régulières est celle utilisée par la base de données. Dans le cas SQLite, qui ne supporte pas nativement une recherche par expression régulière, la syntaxe est celle du module Python re.

Exemple:

Billet.objects.get(title__regex=r'^(An?|The) +')

Equivalents SQL:

SELECT ... WHERE title REGEXP BINARY '^(An?|The) +'; -- MySQL

SELECT ... WHERE REGEXP_LIKE(title, '^(an?|the) +', 'c'); -- Oracle

SELECT ... WHERE title ~ '^(An?|The) +'; -- PostgreSQL

SELECT ... WHERE title REGEXP '^(An?|The) +'; -- SQLite

L'utilisation de chaîne de caractères brutes (par exemple, r'foo' à la place de 'foo') pour la syntaxe des expressions régulières est recommandée.

iregex

Nouveau dans la version en développement de Django

Correspondance d'expression régulière insensible à la casse.

Exemple:

Billet.objects.get(title__iregex=r'^(an?|the) +')

Equivalents SQL:

SELECT ... WHERE title REGEXP '^(an?|the) +'; -- MySQL

SELECT ... WHERE REGEXP_LIKE(title, '^(an?|the) +', 'i'); -- Oracle

SELECT ... WHERE title ~* '^(an?|the) +'; -- PostgreSQL

SELECT ... WHERE title REGEXP '(?i)^(an?|the) +'; -- SQLite

Les recherches par défaut sont exactes

Si vous ne fournissez pas un type de recherche, c'est-à-dire que votre mot-clef argument ne contient pas de double soulignement, la recherche est présumée être une recherche de type exact.

Par exemple, les deux déclarations suivantes sont équivalents:

Blog.objects.get(id__exact=14) # Forme explicite
Blog.objects.get(id=14) # __exact est implicite

Ceci dans un but pratique, car les recherches exact sont les plus courantes.

Le raccourci de recherche pk

Dans un but pratique, Django fourni un type de recherche pk, qui signifie "clef primaire" (primary_key).

Dans l'exemple du modèle Blog, la clef primaire est le champ id. Ces trois déclarations sont donc équivalentes:

Blog.objects.get(id__exact=14) # Forme explicite
Blog.objects.get(id=14) # __exact est implicite
Blog.objects.get(pk=14) # pk implique id__exact

L'utilisation de pk n'est pas limitée aux requêtes __exact. N'importe quel terme de requête peut être combiné avec pk pour effectuer une requête sur la clef primaire du modèle:

# Récupère les entrées de blog ayant pour id 1, 4 et 7
Blog.objects.filter(pk__in=[1,4,7])
# Récupère toutes les entrées ayant un id > 14
Blog.objects.filter(pk__gt=14)

Les recherches pk fonctionnent aussi lors de jointures croisées. Par exemple, ces trois déclarations sont équivalentes:

Billet.objects.filter(blog__id__exact=3) # Forme explicite
Billet.objects.filter(blog__id=3) # __exact est implicite
Billet.objects.filter(blog__pk=3) # __pk implique __id__exact

Note

A cause de ce raccourci, vous ne pouvez pas avoir de champ nommé pk qui ne soit pas la clef primaire du modèle. Cela sera toujours remplacé par le nom de la clef primaire du modèle dans les requêtes.

Les recherches étendues aux relations de liaison

Django offre un moyen puissant et intuitif de "suivre" les relations de liaison dans une recherche, en prenant soin des JOINs SQL pour vous automatiquement, en coulisse. Pour étendre une relation de liaison, utilisez simplement le nom des champs apparentés à travers le modèle, séparés par un double soulignement, jusqu'à ce que vous arriviez au champ désiré.

Cet exemple récupère tout les objets Billet ayant un champ Blog dont le champ nom est 'Beatles Blog':

Billet.objects.filter(blog__nom__exact='Beatles Blog')

Cette étendue peut être aussi profonde que nécessaire.

Cela fonctionne aussi en arrière. Pour référencer une relation de liaison "inversée", utilisez juste le nom du modèle en minuscule.

Cette exemple récupère tous les objets Blog qui ont au moins un champ Billet dont le champ titre contient 'Lennon':

Blog.objects.filter(billet__titre__contains='Lennon')

Parcours des relations multivaluées

Nouveau dans la version en développement de Django

Lorsque l'on filtre un objet en se basant sur un champ ManyToManyField ou sur un champ ForeignKeyField inversé, il y a deux manières différentes de trier qui peuvent vous intéresser. Prenons le cas de la relation Blog/Billet (Blog vers Billet est une relation un-vers-plusieurs). Il peut être intéressant de retrouver les blogs qui ont un billet ayant pour titre "Lennon" et ayant été publié aujourd'hui. Ou alors, on désire retrouver les blogs qui ont un billet ayant pour titre "Lennon" et un autre billet ayant été publié aujourd'hui. Puisque plusieurs billets sont associés à un seul Blog, ces deux requêtes sont possibles et ont un sens dans certaines situations.

Le même type de situation se présente avec un champ ManyToManyField. Par exemple, si un Billet a un champ ManyToManyField nommé etiquettes et que l'on désire trouver les billets ayant une étiquette nommé "musique" ou "groupe", ou que l'on désire utiliser un billet qui contient une étiquette avec le nom "musique" et le statut de "publique".

Pour faire face à ces deux situations, Django utilise une manière cohérente de traitement pour les appels filter() et exclude(). Tout ce qui appartient à un unique appel filter() est appliqué simultanément pour filtrer les éléments correspondants à ces exigences. Les appels successifs à filter() restreindront l'ensemble des objets, mais pour une relation multivaluée, ils s'appliqueront à n'importe quel objet lié au modèle de base, pas nécessairement aux objets sélectionnés par un appel précédent à filter().

Cela peut paraître confus, donc voici un exemple pour clarifier. Pour sélectionner tous les blogs qui ont un billet ayant pour titre "Lennon" et ayant été publié aujourd'hui, on peut écrire:

Blog.objects.filter(billet__titre__contains='Lennon',
        billet__date_publication=datetime.date.today())

Pour sélectionner les blogs qui ont un billet ayant pour titre "Lennon" et, en même temps, un autre billet ayant été publié aujourd'hui, on peut écrire:

Blog.objects.filter(billet__titre__contains='Lennon').filter(
        billet__date_publication=datetime.date.today())

Dans ce second exemple, le premier filtre restreint le QuerySet à tous les blogs liés à ce type de billet. Le second filtre restreint l'ensemble des blogs supplémentaires à ceux qui sont liés au second type de billet. Les billets sélectionnés par le second filtre peuvent être ou non les mêmes que ceux du premier filtre. Ici, on filtre les éléments Blog avec chaque filtre, et non les éléments Billet.

Tout ce comportement s'applique aussi à exclude() : toutes les conditions d'une seule déclaration exclude() sont appliquées à une seule instance (si ces conditions concernent la même relation multivaluée). Les conditions dans les appels à filter() ou exclude() suivants, qui feront référence à la même relation, peuvent filtrer sur différents objets liés.

Échapper les symboles pourcent et soulignement dans une déclaration LIKE

Les recherches de champ équivalentes aux déclarations LIKE de SQL (iexact, contains, icontains, startswith, istartswith, endswith and iendswith) échapperont automatiquement les deux caractères spéciaux utilisés dans la déclaration LIKE, le pourcent et le soulignement. (Dans une déclaration LIKE, le symbole pourcent représente un joker pour plusieurs caractères et un soulignement représente un joker pour un unique caractère.)

Cela signifie que les choses fonctionnent intuitivement, il n'y a pas de faille dans l'abstraction. Par exemple, pour récupérer toutes les entrées contenant un signe pourcent, utilisez simplement le signe pourcent comme tout autre caractère:

Billet.objects.filter(titre__contains='%')

Django place les guillemets pour vous. La requête SQL résultante devrait ressembler à quelque chose comme ca:

SELECT ... WHERE titre LIKE '%\%%';

Les soulignements fonctionnent pareillement. Les symboles pourcent et soulignement sont gérés pour vous de manière transparante.

Les QuerySets et leurs caches

Chaque QuerySet contient un cache afin de minimiser le nombre d'accès à la base de données. Il est important de comprendre leurs fonctionnement pour écrire le code le plus efficace.

Dans tout nouveau QuerySet, le cache est vide. A la première évaluation de celui-ci, une requête est exécutée sur la base, Django enregistre le résultat de la requête dans le cache du QuerySet et retourne le résultat explicitement demandé (par exemple, le prochain élément, si le QuerySet est itéré). Les évaluations futures du QuerySet réutiliseront les résultats contenus dans le cache.

Il est important de se souvenir du comportement du cache, car cela peut vous jouer un mauvais tour si vous n'utilisez pas le QuerySet correctement. Par exemple, les lignes suivantes vont créer deux QuerySet, les évaluer, et les jeter:

print [e.titre for e in Billet.objects.all()]
print [e.date_publication for e in Billet.objects.all()]

Cela signifie que la même requête sera exécutée deux fois, doublant ainsi la charge de la base de données. Aussi, il y a une possibilité que les deux listes n'incluent pas les mêmes enregistrements de la base, si un enregistrement Billet a été ajouté ou supprimé entre les deux requêtes.

Pour éviter ce problème, enregistrez simplement le QuerySet et réutilisez le:

queryset = Billet.objects.all()
print [p.titre for p in queryset] # Evalue le QuerySet.
print [p.date_publication for p in queryset] # Réutilise le cache de l'évaluation précédente.

Comparaison d'objets

Pour comparer deux instances d'un modèle, utilisez simplement l'opérateur standard de comparaison de Python, le signe double égal: ==. En coulisse, ceci comparera les clefs primaires des deux instances.

Appliqué à l'exemple Billet ci-dessus, les deux déclarations suivantes sont équivalentes:

some_Billet == other_Billet
some_Billet.id == other_Billet.id

Si la clef primaire du modèle n'est pas appelée id, pas de problème. La comparaison s'effectuera toujours selon la clef primaire, quelque soit son nom. Par exemple, si la clef primaire d'un modèle est appelée nom, ces deux déclarations sont équivalentes:

some_obj == other_obj
some_obj.nom == other_obj.nom

Recherche complexe avec les objets Q

Les requêtes avec argument mot-clef -- dans filter(), etc. -- sont assemblées à l'aide d'un "ET" (AND). Si vous désirez exécuter des requêtes plus complexes (par exemple, des requêtes avec "OU" (OR)), vous pouvez utilisez les objets Q.

Un objet Q (django.db.models.Q) est un objet utilisé pour encapsuler une collection d'argument mot-clef. Ces arguments mot-clé sont spécifiés comme pour la recherche sur champs ci-dessus.

Par exemple, cet objet Q encapsule une unique requête LIKE:

Q(question__startswith='What')

Les objets Q peuvent être combinés en utilisant les opérateurs & et |. Lorsqu'un opérateur est utilisé sur deux objets Q, il retourne un nouvel objet Q.

Par exemple, cette déclaration retourne un unique objet Q représentant l'union des deux requêtes "question__startswith":

Q(question__startswith='Qui') | Q(question__startswith='Quoi')

Ce qui est équivalent à la clause WHERE SQL suivante:

WHERE question LIKE 'Qui%' OR question LIKE 'Quoi%'

Vous pouvez composer les déclarations d'une complexité arbitraire en combinant les objets Q avec les opérateurs & et |. Vous pouvez aussi utiliser des parenthèses de regroupement.

Nouveau dans la version en développement de Django: Les objets``Q`` peuvent aussi être niés en utilisant l'opérateur ~, permettant ainsi aux recherches combinées d'être combinées avec des requêtes normales et niées (NOT):

Q(question__startswith='Who') | ~Q(date_publication__year=2005)

Chaque fonction de recherche qui prends des arguments mots-clefs (par exemple, filter(), exclude(), get()) peut aussi avoir un ou plusieurs objets Q en tant qu'argument positionnel (non-nommé). Si vous fournissez plusieurs arguments objet Q à la fonction de recherche, les arguments seront assemblés à l'aide d'un "ET" (AND). Par exemple:

Sondage.objects.get(
    Q(question__startswith='Qui'),
    Q(date_publication=date(2005, 5, 2)) | Q(date_publication=date(2005, 5, 6))
)

... traduit grossièrement en requête SQL:

SELECT * from sondage WHERE LIKE 'Qui%'
    AND (date_publication = '2005-05-02' OR date_publication = '2005-05-06')

Les fonctions de recherches peuvent mixer l'utilisation des objets Q et des arguments mots-clefs. Tous les arguments fournis à une fonction de recherche (qu'ils soient des arguments mots-clefs ou des objets Q) sont assemblés par un "ET" ensemble. Cependant, si un objet Q est fourni, il doit précéder la définition de tout argument mot-clef. Par exemple:

Sondage.objects.get(
    Q(date_publication=date(2005, 5, 2)) | Q(date_publication=date(2005, 5, 6)),
    question__startswith='Qui')

... serait une requête valide, équivalente à l'exemple précédent. Mais:

# REQUETE INVALIDE
Sondage.objects.get(
    question__startswith='Qui',
    Q(date_publication=date(2005, 5, 2)) | Q(date_publication=date(2005, 5, 6)))

... ne serait pas valide.

Voir la page d'exemples de recherche par union pour plus d'exemples.

Objets apparentés

Lorsque vous définissez une relation de liaison dans un modèle (c-à-d, un champ ForeignKey, OneToOneField, ou ManyToManyField), les instances de ce modèle auront une API pratique pour accéder aux objets apparentés.

Appliqué aux modèles présents en haut de cette page, par exemple, un objet Billet nommé e peut accéder à son objet Blog associé en utilisant l'attribut blog: e.blog.

(En coulisse, cette fonctionnalité est implémentée à l'aide des descripteurs Python. Cela ne doit pas être très important pour vous, mais nous en parlons pour les curieux.)

Django crée aussi des accesseurs d'API pour les "autres" côtés de la relation de liaison, comme le lien entre l'objet apparenté et le modèle définissant la relation de liaison. Par exemple, un objet Blog nommé b a accès à une liste de tous les objets Billet liés via l'attribut Billet_set: b.Billet_set.all().

Tous les exemples de cette section utilisent les modèles Blog, Author et Billet définis en haut de cette page.

Les relations un-vers-plusieurs

En avant

Si un modèle a un champ ForeignKey, les instances de ce modèles auront accès à l'objet apparenté (étranger) via un simple attribut du modèle.

Exemple:

e = Billet.objects.get(id=2)
e.blog # Retourne l'objet Blog apparenté.

On peut accéder et modifier des données via un attribut clef étrangère. Comme on peut s'y attendre, les changements sur la clé étrangère ne sont enregistrés que lors de l'appel à save(). Exemple:

e = Billet.objects.get(id=2)
e.blog = un_autre_blog
e.save()

Si un champ ForeignKey a l'option null=True (c-à-d, s'il accepte les valeurs NULL), on peut lui assigner la valeur None. Exemple:

e = Billet.objects.get(id=2)
e.blog = None
e.save() # "UPDATE blog_billet SET blog_id = NULL ...;"

L'accès en avant à une relation un-vers-plusieurs met en cache l'objet concerné par la relation. Les accès ultérieurs à la clé étrangère sur le même objet utilisent le cache. Exemple:

e = Billet.objects.get(id=2)
print e.blog  # Interroge la base de données pour retrouver le Blog associé.
print e.blog  # N'interroge pas la base de données. Utilise la version en cache.

On peut noter que la méthode select_related() du QuerySet remplit récursivement le cache de toutes les relations un-vers-plusieurs en avance. Exemple:

e = Billet.objects.select_related().get(id=2)
print e.blog  # N'interroge pas la base de données. Utilise la version en cache.
print e.blog  # N'interroge pas la base de données. Utilise la version en cache.

La méthode select_related() est documentée dans la section Les méthodes des QuerySets qui renvoient d'autres QuerySets ci-dessus.

En arrière

Si un modèle possède un champ ForeignKey, les instances du modèle de la clé étrangère auront accès à un Manager qui retourne toutes les instances du premier modèle. Par défaut, ce Manager est nommé FOO_set, où FOO est le nom du modèle source, en minuscules. Ce Manager retourne des QuerySets, qui peuvent être filtrés et manipulés comme décrit dans la section Récupération des objets ci-dessus.

Exemple:

b = Blog.objects.get(id=1)
b.billet_set.all() # Retourne tous les objets Billet reliés au Blog.

# b.entry_set est un Manager qui retourne des QuerySets.
b.billet_set.filter(titre__contains='Lennon')
b.billet_set.count()

Il est possible de redéfinir le nom de FOO_set en paramétrant l'attribut related_name dans la définition de ForeignKey(). Par exemple, si le modèle Billet était modifié par blog = ForeignKey(Blog, related_name='billets'), le code de l'exemple ci-dessus ressemblerait à ceci:

b = Blog.objects.get(id=1)
b.billets.all() # retourne tous les objets Billet reliés au Blog.

# b.entry_set est un Manager qui retourne des QuerySets.
b.billets.filter(titre__contains='Lennon')
b.billets.count()

Il est impossible d'accéder au Manager d'une clé étrangère en arrière à partir de la classe. L'accès doit se faire à partir d'une instance. Exemple:

Blog.billet_set # lève l'exception AttributeError: "Manager must be accessed via instance".

En plus des méthodes du QuerySet définies dans la section Récupération des objets ci-dessus, le Manager de ForeignKey possède ces méthodes additionnelles:

  • add(obj1, obj2, ...): Ajoute l'objet du modèle spécifié à l'ensemble des objets reliés.

    Exemple:

    b = Blog.objects.get(id=1)
    e = Billet.objects.get(id=234)
    b.billet_set.add(e) # associe le Billet e avec le Blog b
    
  • create(**kwargs): Crée un nouvel objet, l'enregistre et l'ajoute à l'ensemble des objets reliés. Renvoie l'objet nouvellement créé.

    Exemple:

    b = Blog.objects.get(id=1)
    e = b.billet_set.create(titre='Bonjour', texte='Coucou', date_publication=datetime.date(2005, 1, 1))
    # Pas besoin d'appeler e.save() à cet endroit, le billet a déjà été enregistré.
    

    Ceci est équivalent à (mais plus simple que):

    b = Blog.objects.get(id=1)
    e = Billet(blog=b, titre='Bonjour', texte='Coucou', date_publication=datetime.date(2005, 1, 1))
    e.save()
    

    Notez qu'il n'est pas nécessaire de préciser l'argument mot-clef du modèle définissant la relation. Dans l'exemple précédent, on ne passe pas le paramètre blog à create(). Django trouve lui-même que le champ blog du nouvel objet Billet doit prendre la valeur b.

  • remove(obj1, obj2, ...): Retire les objets du modèle spécifié de l'ensemble des objets reliés.

    Exemple:

    b = Blog.objects.get(id=1)
    e = Billet.objects.get(id=234)
    b.billet_set.remove(e) # Désassocie le Billet e du Blog b.
    

    Afin d'éviter l'inconsistance de la base de données, cette méthode existe seulement sur les objets ForeignKeynull=True. Si le champ relié ne peut pas prendre la valeur None (NULL), alors un objet ne peut peut pas être retiré d'une relation sans être ajouté à une autre. Dans l'exemple ci-dessus, retirer e de b.billet_set est équivalent à écrire e.blog = None, et puisque l'attribut ForeignKey blog n'a pas null=True, cela est invalide.

  • clear(): Retire tous les objets de l'ensemble des objets reliés.

    Exemple:

    b = Blog.objects.get(id=1)
    b.billet_set.clear()
    

    Noter que cela ne supprime pas les objets reliés, cela les dissocie juste.

    Tout comme remove(), clear() est uniquement disponible sur les champs ForeignKeynull=True.

Pour affecter les membres d'un ensemble relié d'un seul coup, il suffit de lui affecter un objet itérable. Exemple:

b = Blog.objects.get(id=1)
b.billet_set = [e1, e2]

Si la méthode clear() est disponible, tous les objets pré-existant seront retirés de billet_set avant d'ajouter tous les objets contenus dans l'itérable (dans ce cas, une liste). Si la méthode clear() n'est pas disponible, tous les objets contenus dans l'ité