# Documentation Django 6.0.7

# Création de requêtes

Une fois les modèles de données créés, Django offre automatiquement une API d’abstraction de base de données qui permet de créer, obtenir, mettre à jour et supprimer des objets. Ce document explique comment utiliser cette API. Consultez la référence des modèles de données pour des détails complets sur toutes les options d’interrogation des modèles.

À travers ce guide (et dans la référence), nous nous référons aux modèles suivants, qui forment une application de blog :

    from datetime import date

    from django.db import models

    class Blog(models.Model):
        name = models.CharField(max_length=100)
        tagline = models.TextField()

        def __str__(self):
            return self.name

    class Author(models.Model):
        name = models.CharField(max_length=200)
        email = models.EmailField()

        def __str__(self):
            return self.name

    class Entry(models.Model):
        blog = models.ForeignKey(Blog, on_delete=models.CASCADE)
        headline = models.CharField(max_length=255)
        body_text = models.TextField()
        pub_date = models.DateField()
        mod_date = models.DateField(default=date.today)
        authors = models.ManyToManyField(Author)
        number_of_comments = models.IntegerField(default=0)
        number_of_pingbacks = models.IntegerField(default=0)
        rating = models.IntegerField(default=5)

        def __str__(self):
            return self.headline

## Création d’objets

Pour représenter des données d’une table de base de données en objets Python, Django utilise un système intuitif : une classe de modèle représente une table de base de données, et une instance de cette classe représente un enregistrement particulier dans la table de base de données.

Pour créer un objet, créez une instance de la classe de modèle en utilisant des paramètres nommés, puis appelez `save()` pour l’enregistrer dans la base de données.

En supposant que les modèles se trouvent dans un fichier `models.py` dans une application Django `blog`, voici un exemple :

    >>> from blog.models import Blog
    >>> b = Blog(name="Beatles Blog", tagline="All the latest Beatles news.")
    >>> b.save()

Ceci exécute une commande SQL `INSERT` en arrière-plan. Django ne sollicite pas la base de données tant que vous n’appelez pas explicitement `save()`.

La méthode `save()` ne renvoie aucune valeur.

Voir aussi

`save()` accepte un certain nombre d’options avancées décrites ailleurs. Consultez la documentation de `save()` pour les détails complets.

Pour créer et enregistrer un objet en une seule étape, utilisez la méthode `create()`.

## Enregistrement des modifications d’objets

Pour enregistrer les modifications d’un objet existant dans la base de données, utilisez `save()`.

Considérant une instance `b5` d’un `Blog` ayant déjà été enregistrée dans la base de données, cet exemple modifie son nom et met à jour son enregistrement dans la base de données :

    >>> b5.name = "New name"
    >>> b5.save()

Ceci génère une commande SQL `UPDATE` en arrière-plan. Django ne sollicite pas la base de données tant que vous n’appelez pas explicitement `save()`.

### Enregistrement des champs `ForeignKey` et `ManyToManyField`

La mise à jour d’un champ `ForeignKey` fonctionne exactement de la même façon que l’enregistrement d’un champ normal, attribuez un objet du bon type au champ en question. Cet exemple met à jour l’attribut `blog` d’une instance `Entry` nommée `entry`, en supposant que les instances appropriées de `Entry` et de `Blog` sont déjà enregistrées dans la base de données (afin de pouvoir être récupérées ci-dessous) :

    >>> from blog.models import Blog, Entry
    >>> entry = Entry.objects.get(pk=1)
    >>> cheese_blog = Blog.objects.get(name="Cheddar Talk")
    >>> entry.blog = cheese_blog
    >>> entry.save()

La mise à jour d’un champ `ManyToManyField` fonctionne un peu différemment ; utilisez la méthode `add()` du champ pour ajouter un enregistrement à la relation. Cet exemple ajoute l’instance `Author` `joe` à l’objet `entry`:

    >>> from blog.models import Author
    >>> joe = Author.objects.create(name="Joe")
    >>> entry.authors.add(joe)

Pour ajouter plusieurs enregistrements à un champ `ManyToManyField` d’un seul coup, indiquez plusieurs paramètres dans l’appel à `add()`, comme ceci :

    >>> john = Author.objects.create(name="John")
    >>> paul = Author.objects.create(name="Paul")
    >>> george = Author.objects.create(name="George")
    >>> ringo = Author.objects.create(name="Ringo")
    >>> entry.authors.add(john, paul, george, ringo)

Django se plaint si vous essayez d’assigner ou d’ajouter un objet de type inapproprié.

## Sélection d’objets

Pour extraire des objets de la base de données, construisez un `QuerySet` via un `Manager` de la classe de votre modèle.

Un `QuerySet` représente une collection d’objets de la base de données. Il peut comporter zéro, un ou plusieurs filtres. Les filtres réduisent les résultats de requêtes en fonction des paramètres donnés. En termes SQL, un `QuerySet` équivaut à une commande `SELECT` et un filtre correspond à une clause restrictive telle que `WHERE` ou `LIMIT`.

On obtient un `QuerySet` en utilisant le `Manager` du modèle. Chaque modèle a au moins un `Manager` ; il s’appelle `objects` par défaut. On y accède directement via la classe du modèle, comme ceci :

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

Note

Un `Manager` n’est accessible que par les classes de modèle et non par les instances de modèle, afin d’accentuer la séparation entre les opérations sur les « tables » et les opérations sur les « enregistrements ».

Le `Manager` est la source principale des jeux de requête d’un modèle. Par exemple, `Blog.objects.all()` renvoie un `QuerySet` contenant tous les objets `Blog` de la base de données.

### Sélection de tous les objets

La façon la plus simple d’obtenir des objets d’une table est de tous les sélectionner. Pour ce faire, utilisez la méthode `all()` d’un `Manager`:

    >>> all_entries = Entry.objects.all()

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

### Sélection d’objets spécifiques avec les filtres

Le `QuerySet` renvoyé par `all()` contient tous les objets de la table de la base de données. Mais le plus souvent, seul un sous-ensemble de tous les objets devra être sélectionné.

Pour créer un tel sous-ensemble, vous affinez le `QuerySet` initial en y ajoutant des filtres de conditions. Les deux façons les plus utilisées pour affiner un `QuerySet` sont :

`filter(**kwargs)`

  Renvoie un nouveau `QuerySet` contenant les objets qui répondent aux paramètres de recherche donnés.

`exclude(**kwargs)`

  Renvoie un nouveau `QuerySet` contenant les objets qui ne répondent pas aux paramètres de recherche donnés.

Les paramètres de recherche (`**kwargs` dans les définitions de fonction ci-dessus) doivent être dans le format décrit dans Recherches dans les champs ci-dessous.

Par exemple, pour obtenir un `QuerySet` des articles de blog de l’année 2006, utilisez `filter()` comme ceci :

    Entry.objects.filter(pub_date__year=2006)

Avec la classe de gestionnaire par défaut, c’est équivalent à :

    Entry.objects.all().filter(pub_date__year=2006)

#### Enchaînement des filtres

Le résultat de l’affinage d’un `QuerySet` est lui-même un `QuerySet`, il est donc possible d’enchaîner les filtrages successifs. Par exemple :

    >>> Entry.objects.filter(headline__startswith="What").exclude(
    ...     pub_date__gte=datetime.date.today()
    ... ).filter(pub_date__gte=datetime.date(2005, 1, 30))

À partir du `QuerySet` initial de toutes les lignes dans la base de données, le code ci-dessus ajoute un filtre, puis une exclusion, puis un autre filtre. Le résultat final est un `QuerySet` contenant tous les enregistrements ayant un titre (`headline`) commençant par « What », ayant été publiés entre le 30 janvier 2005 et aujourd’hui.

#### Les `QuerySet` filtrés sont uniques

À chaque fois que vous affinez un `QuerySet`, vous obtenez un `QuerySet` tout neuf qui n’est lié d’aucune manière au `QuerySet` précédent. Chaque affinage crée un `QuerySet` séparé et distinct qui peut être stocké, utilisé et réutilisé.

Exemple :

    >>> q1 = Entry.objects.filter(headline__startswith="What")
    >>> q2 = q1.exclude(pub_date__gte=datetime.date.today())
    >>> q3 = q1.filter(pub_date__gte=datetime.date.today())

Ces trois jeux de requête sont séparés. Le premier est un `QuerySet` de base contenant tous les enregistrements qui contiennent un titre commençant par « What ». Le second est un sous-ensemble du premier, avec un critère supplémentaire qui exclut les enregistrements dont le champ `pub_date` est aujourd’hui ou dans le futur. Le troisième est un sous-ensemble du premier, avec un critère supplémentaire qui sélectionne uniquement les enregistrements dont le champ `pub_date` est aujourd’hui ou dans le futur. Le `QuerySet` initial (`q1`) n’est pas affecté par le processus d’affinage.

#### Les objects `QuerySet` sont différés

Les objets `QuerySet` sont différés (« lazy ») ; la création d’un `QuerySet` ne génère aucune activité au niveau de la base de données. Vous pouvez empiler les filtres toute la journée, Django ne lance aucune requête tant que le `QuerySet` n’est pas évalué. Regardez cet exemple :

    >>> q = Entry.objects.filter(headline__startswith="What")
    >>> q = q.filter(pub_date__lte=datetime.date.today())
    >>> q = q.exclude(body_text__icontains="food")
    >>> print(q)

Malgré le fait que ceci ressemble à trois interrogations de la base de données, en réalité une seule interrogation est faite, à la dernière ligne (`print(q)`). En général, les résultats d’un `QuerySet` ne sont récupérés de la base de données que lorsque vous les « demandez ». Lorsque cela arrive, le `QuerySet` est évalué en accédant à la base de données. Pour plus de détails sur le moment exact où l’évaluation a lieu, consultez Quand les objets QuerySet sont évalués.

### Sélection d’un objet unique avec `get()`

`filter()` renvoie toujours un `QuerySet`, même si la requête ne renvoie qu’un seul objet ; dans ce cas, le `QuerySet` ne contiendra simplement qu’un seul élément.

Si vous savez qu’un seul objet correspondra à votre requête, vous pouvez utiliser la méthode `get()` d’un `Manager` qui renvoie directement l’objet :

    >>> one_entry = Entry.objects.get(pk=1)

N’importe quelle expression de recherche peut être utilisée avec `get()`, comme pour `filter()`. Voir Recherches dans les champs ci-dessous.

Notez qu’il y a une différence entre l’utilisation de `get()` et celle de `filter()` avec le segment `[0]`. Si aucun résultat ne correspond à la requête, `get()` génère une exception `DoesNotExist`. Cette exception est un attribut de la classe de modèle sur laquelle la requête est appliquée ; ainsi, dans le code ci-dessus, s’il n’existe pas d’objet `Entry` avec une clé primaire de 1, Django génère l’exception `Entry.DoesNotExist`.

De même, Django réagit si plus d’un élément correspond à la requête `get()`. Dans ce cas, il lève l’exception `MultipleObjectsReturned` qui est également un attribut de la classe de modèle elle-même.

### Autres méthodes de `QuerySet`

La plupart du temps, vous utiliserez `all()`, `get()`, `filter()` et `exclude()` lorsque vous aurez besoin de sélectionner des objets dans la base de données. Cependant, c’est loin d’être tout ; consultez la référence de l’API QuerySet pour une liste complète des diverses méthodes de `QuerySet`.

### Limitation des `QuerySet`

Utilisez un sous-ensemble de la syntaxe Python de segmentation des listes pour restreindre un `QuerySet` à un certain nombre de résultats. C’est l’équivalent des clauses SQL `LIMIT` et `OFFSET`.

Par exemple, ceci renvoie les 5 premiers objets (`LIMIT 5`) :

    >>> Entry.objects.all()[:5]

Ceci renvoie du sixième au dixième objet (`OFFSET 5 LIMIT 5`) :

    >>> Entry.objects.all()[5:10]

Les index négatifs (ex. : `Entry.objects.all()[-1]`) ne sont pas pris en charge.

De manière générale, la segmentation d’un `QuerySet` renvoie un nouveau `QuerySet` ; la requête n’est pas évaluée, sauf si vous utilisez le paramètre `step` de la syntaxe de segmentation de Python. Par exemple, ceci exécuterait effectivement la requête pour renvoyer une liste d’un objet sur 2 parmi les 10 premiers :

    >>> Entry.objects.all()[:10:2]

Tout filtre ou tri subséquent d’un jeu de requête segmenté est interdit en raison de la nature ambiguë de son éventuel fonctionnement.

Pour obtenir un seul objet plutôt qu’une liste (ex. : `SELECT foo FROM bar LIMIT 1`), utilisez un index au lieu d’un segment. Par exemple, ceci renvoie le premier objet `Entry` de la base de données, après avoir trié les objets alphabétiquement par titre (`headline`) :

    >>> Entry.objects.order_by("headline")[0]

C’est grossièrement équivalent à :

    >>> Entry.objects.order_by("headline")[0:1].get()

Notez toutefois que si aucun objet ne correspond à la requête, une exception `IndexError` est générée dans le premier cas, tandis que dans le second cas, c’est l’exception `DoesNotExist` qui sera générée. Voir `get()` pour plus de détails.

### Recherches dans les champs

La recherche dans les champs est ce qui constitue le cœur des clauses SQL `WHERE`. La syntaxe s’exprime par des paramètres nommés dans les méthodes `filter()`, `exclude()` et `get()` de `QuerySet`.

Les paramètres nommés de base de ces requêtes prennent la forme `champ__typerequete=valeur` (il s’agit d’un double soulignement). Par exemple :

    >>> Entry.objects.filter(pub_date__lte="2006-01-01")

peut être grossièrement traduit en code SQL comme :

    SELECT * FROM blog_entry WHERE pub_date <= '2006-01-01';

Comment est-ce possible ?

Python permet de définir des fonctions acceptant des paramètres nom-valeur arbitraires dont les noms et les valeurs sont évalués au moment de l’exécution. Pour plus d’informations, consultez Keyword Arguments dans le tutoriel Python officiel.

Le champ indiqué dans une recherche doit correspondre au nom d’un champ de modèle. Il existe cependant une exception ; dans le cas d’une clé étrangère `ForeignKey`, vous pouvez indiquer le nom du champ additionné du suffixe `_id`. Dans ce cas, le paramètre valeur doit contenir la valeur brute de la clé primaire du modèle étranger. Par exemple :

    >>> Entry.objects.filter(blog_id=4)

Si vous fournissez un paramètre nommé non valide, une fonction de recherche signalera une exception `TypeError`.

L’API de base de données gère une vingtaine de types de recherche ; une référence complète se trouve dans la référence des recherches de champs. Pour vous donner une idée de ce qui est possible, voici quelques recherches parmi les plus fréquemment utilisées :

`exact`

  Une correspondance « exacte ». Par exemple :

      >>> Entry.objects.get(headline__exact="Cat bites dog")

  Produit le code SQL ressemblant à :

      SELECT ... WHERE headline = 'Cat bites dog';

  Si vous n’indiquez pas de type de recherche, c’est-à-dire si votre paramètre nommé ne contient pas de double soulignement, Django considère que le type de recherche est `exact`.

  Par exemple, les deux lignes suivantes sont équivalentes :

      >>> Blog.objects.get(id__exact=14)  # Explicit form
      >>> Blog.objects.get(id=14)  # __exact is implied

  Il s’agit d’un raccourci commode, puisque les recherches `exact` sont les cas les plus courants.

`iexact`

  Une recherche insensible à la casse. Ainsi, la requête :

      >>> Blog.objects.get(name__iexact="beatles blog")

  Aurait pour occurrence un `Blog` intitulé `"Beatles Blog"`, `"beatles blog"` ou même `"BeAtlES blOG"`.

`contains`

  Test d’inclusion sensible à la casse. Par exemple :

      Entry.objects.get(headline__contains="Lennon")

  Pourrait être traduit grosso modo par ce code SQL :

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

  Notez que le titre (`headline`) `'Today Lennon honored'` correspondrait à cette recherche, mais pas `'today lennon honored'`.

  Il existe aussi une version insensible à la casse, `icontains`.

`startswith`, `endswith`

  Recherche « commençant par » et « finissant par », respectivement. Il en existe aussi des versions insensibles à la casse : `istartswith` et `iendswith`.

Encore une fois, ce n’est qu’un survol. Une référence complète est disponible dans la référence des recherches de champ.

### Recherches traversant les relations

Django offre une approche puissante et intuitive pour « suivre » les relations dans les recherches, se chargeant automatiquement des `JOIN` SQL en arrière-plan. Pour atteindre une relation, utilisez les noms de champ qui servent de relation vers d’autres modèles, séparés par des doubles soulignements, jusqu’à atteindre le champ souhaité.

Cet exemple sélectionne tous les objets `Entry` d’un `Blog` ayant pour `name` la valeur `'Beatles Blog'`:

    >>> Entry.objects.filter(blog__name="Beatles Blog")

Ce mécanisme de traversée de relations peut être aussi profond que vous le souhaitez.

It works backwards, too. While it can be customized by setting `related_query_name`, by default you refer to a « reverse » relationship in a lookup using the lowercase name of the model.

Cet exemple sélectionne tous les objets `Blog` ayant au moins une `Entry` ayant un `headline` contenant `'Lennon'`:

    >>> Blog.objects.filter(entry__headline__contains="Lennon")

Si vous filtrez à travers plusieurs relations et qu’un des modèles intermédiaires n’a pas de valeur répondant à la condition du filtre, Django le traite comme s’il y avait là un objet vide (toutes les valeurs sont `NULL`), mais valide. C’est-à-dire qu’aucune erreur ne sera signalée. Par exemple, dans ce filtre :

    Blog.objects.filter(entry__authors__name="Lennon")

(partant du principe qu’il existe un modèle `Author` lié), s’il n’y a pas de `author` associé à une `entry`, ce sera traité comme s’il n’y avait pas non plus de `name`, plutôt que de signaler une erreur à cause de l”`author` manquant. Normalement, c’est bien le comportement souhaité. Le seul cas où ça pourrait porter à confusion, c’est quand vous utilisez `isnull`. Ainsi :

    Blog.objects.filter(entry__authors__name__isnull=True)

renvoie les objets `Blog` ayant un `author` avec le `name` vide ainsi que tous ceux ayant un `author` vide dans l”`entry`. Si vous ne voulez pas de cette deuxième catégorie d’objets, vous pourriez écrire :

    Blog.objects.filter(entry__authors__isnull=False, entry__authors__name__isnull=True)

#### Traversée des relations multivaluées

Lors de la traversée d’un champ `ManyToManyField` ou d’un clé `ForeignKey` inverse (comme de `Blog` à `Entry`), le filtrage sur plusieurs attributs pose la question de savoir si chaque attribut doit correspondre pour chaque objet lié. On pourrait rechercher des blogs qui ont un article en 2008 avec « “Lennon” » dans son titre, ou plus simplement des blogs qui ont un article en 2008 de même que des articles plus vieux ou plus récentes qui contiennent « “Lennon” » dans leur titre.

Pour sélectionner tous les blogs contenant au moins un article en 2008 ayant « Lennon » dans son titre (le même article devant satisfaire les deux conditions), il faudrait écrire

    Blog.objects.filter(entry__headline__contains="Lennon", entry__pub_date__year=2008)

Sinon, pour effectuer une requête plus permissive sélectionnant tous les blogs ayant un ou plusieurs articles avec « Lennon » dans leur titre et un ou plusieurs articles en 2008, on écrirait

    Blog.objects.filter(entry__headline__contains="Lennon").filter(
        entry__pub_date__year=2008
    )

Supposons qu’il n’y ait qu’un seul blog ayant à la fois des articles contenant « Lennon » et des articles de 2008, mais qu’aucun des articles de 2008 ne contienne « Lennon ». La première requête ne renverrait aucun blog, mais la seconde renverrait ce blog (et cela parce que les lignes sélectionnées par le second filtre ne sont pas forcément les mêmes que celles du premier filtre ; chaque instruction de filtrage agit sur les éléments `Blog`, pas sur les éléments `Entry` ). En résumé, si toutes les conditions doivent s’appliquer sur les mêmes objets liés, alors chaque filtre doit être contenu dans un même appel à `filter()`.

Note

Comme la seconde requête (plus permissive) enchaîne plusieurs filtres, elle effectue plusieurs jointures vers le modèle principal, ce qui peut provoquer des doublons.

    >>> from datetime import date
    >>> beatles = Blog.objects.create(name="Beatles Blog")
    >>> pop = Blog.objects.create(name="Pop Music Blog")
    >>> Entry.objects.create(
    ...     blog=beatles,
    ...     headline="New Lennon Biography",
    ...     pub_date=date(2008, 6, 1),
    ... )
    <Entry: New Lennon Biography>
    >>> Entry.objects.create(
    ...     blog=beatles,
    ...     headline="New Lennon Biography in Paperback",
    ...     pub_date=date(2009, 6, 1),
    ... )
    <Entry: New Lennon Biography in Paperback>
    >>> Entry.objects.create(
    ...     blog=pop,
    ...     headline="Best Albums of 2008",
    ...     pub_date=date(2008, 12, 15),
    ... )
    <Entry: Best Albums of 2008>
    >>> Entry.objects.create(
    ...     blog=pop,
    ...     headline="Lennon Would Have Loved Hip Hop",
    ...     pub_date=date(2020, 4, 1),
    ... )
    <Entry: Lennon Would Have Loved Hip Hop>
    >>> Blog.objects.filter(
    ...     entry__headline__contains="Lennon",
    ...     entry__pub_date__year=2008,
    ... )
    <QuerySet [<Blog: Beatles Blog>]>
    >>> Blog.objects.filter(
    ...     entry__headline__contains="Lennon",
    ... ).filter(
    ...     entry__pub_date__year=2008,
    ... )
    <QuerySet [<Blog: Beatles Blog>, <Blog: Beatles Blog>, <Blog: Pop Music Blog]>

Note

Le comportement de `filter()` pour les requêtes qui couvrent des relations multi-valeurs” tel que décrit ci-dessus, n’est pas implémenté de la même manière pour `exclude()`. Au contraire, les conditions de restriction indiquées lors qu’un appel à `exclude()` ne doivent pas nécessairement s’appliquer au même élément.

Par exemple, la requête ci-dessous exclurait les blogues qui contiennent à la fois « Lennon » dans le titre et des entrées publiées en 2008

    Blog.objects.exclude(
        entry__headline__contains="Lennon",
        entry__pub_date__year=2008,
    )

Cependant, contrairement au comportement lors de l’utilisation de `filter()`, cela ne sélectionnera pas les blogs basés sur des entrées qui satisfont les deux conditions en même temps. Pour faire cela, c’est à dire pour sélectionner tous les blogs qui ne contiennent pas d’entrées publiées avec « Lennon » et qui ont été publiées en 2008, vous devez faire deux requêtes

    Blog.objects.exclude(
        entry__in=Entry.objects.filter(
            headline__contains="Lennon",
            pub_date__year=2008,
        ),
    )

### Les filtres peuvent référencer les champs d’un modèle

Dans les exemples donnés jusqu’ici, nous avons construit des filtres comparant la valeur d’un champ de modèle avec une constante. Mais qu’en est-il si vous souhaitez comparer la valeur d’un champ de modèle avec un autre champ du même modèle ?

Django provides Expressions F() to allow such comparisons. Instances of `F()` act as a reference to a model field within a query. These references can then be used in query filters to compare the values of two different fields on the same model instance.

Par exemple, pour obtenir la liste de tous les articles de blogue ayant reçus plus de commentaires que de « pings », nous construisons un objet `F()` pour référencer le nombre de pings afin d’utiliser cet objet `F()` dans la requête :

    >>> from django.db.models import F
    >>> Entry.objects.filter(number_of_comments__gt=F("number_of_pingbacks"))

Django prend en charge l’utilisation des opérateurs arithmétiques addition, soustraction, multiplication, division, modulo et puissance avec les objets `F()`, aussi bien avec des constantes qu’avec d’autres objets `F()`. Pour obtenir tous les articles de blogue avec plus de deux fois de commentaires que de pings, nous modifions ainsi la requête :

    >>> Entry.objects.filter(number_of_comments__gt=F("number_of_pingbacks") * 2)

Pour trouver tous les articles où la notation de l’article est plus petite que la somme des pings et des commentaires, nous pourrions effectuer cette requête :

    >>> Entry.objects.filter(rating__lt=F("number_of_comments") + F("number_of_pingbacks"))

Vous pouvez aussi employer la notation en double soulignement pour traverser les relations dans un objet `F()`. Un objet `F()` contenant un double soulignement produira toute jointure nécessaire pour accéder aux objets liés. Par exemple, pour obtenir tous les articles dont le nom de l’auteur est égal au nom du blogue, nous pourrions effectuer la requête :

    >>> Entry.objects.filter(authors__name=F("blog__name"))

Pour les champs date et date/heure, il est possible d’ajouter ou de soustraire un objet `timedelta`. L’exemple suivant renvoie tous les articles modifiés plus de 3 jours après avoir été publiés :

    >>> from datetime import timedelta
    >>> Entry.objects.filter(mod_date__gt=F("pub_date") + timedelta(days=3))

Les objets `F()` prennent en charge les opérations bit à bit via `.bitand()`, `.bitor()`, `.bitxor()`, `.bitrightshift()` et `.bitleftshift()`. Par exemple :

    >>> F("somefield").bitand(16)

Oracle

Oracle ne prend pas en charge l’opération bit à bit XOR.

### Les expressions peuvent référencer des transformations

Django prend en charge l’appel à des transformations dans les expressions.

Par exemple, pour trouver tous les objets `Entry` publiés la même année que leur date de dernière modification :

    >>> from django.db.models import F
    >>> Entry.objects.filter(pub_date__year=F("mod_date__year"))

Pour trouver l’année de publication la plus ancienne d’un élément, on peut lancer la requête :

    >>> from django.db.models import Min
    >>> Entry.objects.aggregate(first_published_year=Min("pub_date__year"))

Cette exemple cherche la valeur de l’élément le mieux noté et le nombre total de commentaires de tous les éléments pour chaque année :

    >>> from django.db.models import OuterRef, Subquery, Sum
    >>> Entry.objects.values("pub_date__year").annotate(
    ...     top_rating=Subquery(
    ...         Entry.objects.filter(
    ...             pub_date__year=OuterRef("pub_date__year"),
    ...         )
    ...         .order_by("-rating")
    ...         .values("rating")[:1]
    ...     ),
    ...     total_comments=Sum("number_of_comments"),
    ... )

### Le raccourci de recherche `pk`

Pour des raisons pratiques, Django fournit un raccourci de recherche `pk`, signifiant « primary key » (clé primaire).

Dans le modèle d’exemple `Blog`, la clé primaire est le champ `id`. Ainsi, ces trois lignes sont équivalentes :

    >>> Blog.objects.get(id__exact=14)  # Explicit form
    >>> Blog.objects.get(id=14)  # __exact is implied
    >>> Blog.objects.get(pk=14)  # pk implies id__exact

L’emploi de `pk` n’est pas limité aux requêtes `__exact`; toute expression de requête peut être combinée avec `pk` pour effectuer une requête sur la clé primaire d’un modèle :

    # Get blogs entries with id 1, 4 and 7
    >>> Blog.objects.filter(pk__in=[1, 4, 7])

    # Get all blog entries with id > 14
    >>> Blog.objects.filter(pk__gt=14)

Les recherches sur `pk` fonctionnent également en traversant les relations. Par exemple, ces trois lignes sont équivalentes :

    >>> Entry.objects.filter(blog__id__exact=3)  # Explicit form
    >>> Entry.objects.filter(blog__id=3)  # __exact is implied
    >>> Entry.objects.filter(blog__pk=3)  # __pk implies __id__exact

### Échappement des signes pour cent et des soulignements dans les instructions `LIKE`

Les recherches de champs produisant des instructions SQL `LIKE` (`iexact`, `contains`, `icontains`, `startswith`, `istartswith`, `endswith` et `iendswith`) échappent automatiquement les deux caractères à signification spéciale des instructions `LIKE`: le signe pour cent et le soulignement (dans une instruction `LIKE`, le signe pour cent est utilisé comme caractère de remplacement multiple alors qu’un soulignement est utilisé comme caractère de remplacement unique).

Cela signifie que les choses devraient fonctionner intuitivement, sans faille d’abstraction. Par exemple, pour obtenir la liste de tous les articles contenant un signe pour cent, indiquez le signe pour cent comme pour n’importe quel autre caractère :

    >>> Entry.objects.filter(headline__contains="%")

Django se charge de l’échappement à votre place ; le code SQL résultant ressemblera à quelque chose comme :

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

Même chose pour les soulignements. Aussi bien les signes pour cent que les soulignements sont gérés pour vous de manière transparente.

### Mise en cache et objets `QuerySet`

Chaque `QuerySet` contient un cache pour minimiser l’accès à la base de données. La compréhension de son fonctionnement vous permet d’écrire le code le plus efficace possible.

Pour un nouvel objet `QuerySet`, son cache est vide. À la première évaluation d’un `QuerySet` (et donc qu’une requête de base de données est effectuée), Django enregistre les résultats de la requête dans le cache de l’objet `QuerySet` et renvoie les résultats qui ont été explicitement demandés (par ex. l’élément suivant dans le cas d’une itération du `QuerySet`). Les évaluations suivantes de l’objet `QuerySet` réutilisent les résultats mis en cache.

Gardez à l’esprit ce comportement de mise en cache, car vous pourriez vous faire avoir si vous n’utilisez pas correctement l’objet `QuerySet`. Par exemple, le code suivant crée deux objets `QuerySet`, les évalue et les abandonne :

    >>> print([e.headline for e in Entry.objects.all()])
    >>> print([e.pub_date for e in Entry.objects.all()])

Dans ce cas, la même requête de base de données est exécutée deux fois, ce qui double en pratique la charge sur la base de données. Il est également imaginable que les deux listes ne contiennent pas exactement les mêmes enregistrements de base de données, car un objet `Entry` pourrait avoir été ajouté ou supprimé dans la fraction de seconde entre les deux requêtes.

Pour éviter ce problème, stockez l’objet `QuerySet` et réutilisez-le :

    >>> queryset = Entry.objects.all()
    >>> print([p.headline for p in queryset])  # Evaluate the query set.
    >>> print([p.pub_date for p in queryset])  # Reuse the cache from the evaluation.

#### Quand les objets `QuerySet` ne sont pas mis en cache

Les objets `QuerySet` ne mettent pas toujours leurs résultats en cache. Lorsque seule une partie d’un queryset est évaluée, le cache est consulté mais s’il n’est pas rempli, les éléments renvoyés par la requête suivante ne sont pas mis en cache. Spécifiquement, cela signifie que la restriction d’un jeu de requêtes en employant une segmentation ou un index de liste ne remplira par le cache.

Par exemple, l’interrogation répétée d’un objet `QuerySet` à l’aide d’un index effectue une requête dans la base de données à chaque fois :

    >>> queryset = Entry.objects.all()
    >>> print(queryset[5])  # Queries the database
    >>> print(queryset[5])  # Queries the database again

Cependant, si le jeu de requête complet a déjà été évalué, le cache sera tout de même mis à contribution :

    >>> queryset = Entry.objects.all()
    >>> [entry for entry in queryset]  # Queries the database
    >>> print(queryset[5])  # Uses cache
    >>> print(queryset[5])  # Uses cache

Voici quelques exemples d’autres actions qui déclenchent l’évaluation de tout l’objet `QuerySet` et qui par conséquent remplissent le cache :

    >>> [entry for entry in queryset]
    >>> bool(queryset)
    >>> entry in queryset
    >>> list(queryset)

Note

L’affichage simple d’un `QuerySet` ne remplit pas le cache. Ceci parce que l’appel à `__repr__()` ne renvoie qu’un segment du jeu de requête complet.

## Requêtes asynchrones

Si vous écrivez des vues ou du code asynchrone, vous ne pouvez pas utiliser l’ORM pour des requêtes de la façon qui est décrite ci-dessus, car il n’est pas possible d’appeler du code synchrone bloquant à partir de code asynchrone, cela bloquerait la boucle événementielle (ou plus probablement Django le remarquera et générera l’exception `SynchronousOnlyOperation` pour que ce blocage ne se produise pas).

Heureusement, il est possible de faire de nombreuses requêtes en utilisant les API de requêtes asynchrones de Django. Toute méthode susceptible de bloquer, telle que `get()` ou `delete()`, possède une variante asynchrone (`aget()` ou `adelete()`), et quand vous passez en boucle sur les résultats, vous pouvez alors utiliser l’itération asynchrone (`async for`).

### Requêtes et itération

La manière par défaut de passer en boucle sur une requête - avec `for` - aboutit à une requête de base de données bloquante en arrière-plan du moment que Django charge les résultats au moment de l’itération. Pour corriger cela, vous pouvez exploiter `async for`:

    async for entry in Authors.objects.filter(name__startswith="A"):
        ...

Soyez bien conscient que vous ne pouvez pas non plus exécuter d’autres opérations qui aboutiraient à une itération du jeu de requête, comme par exemple le placer dans un appel à `list()` pour forcer son évaluation (vous pouvez cependant utiliser `async for` dans une compréhension de liste si vous le souhaitez).

Comme les méthodes de `QuerySet` telles que``filter()`` ou `exclude()` n’exécutent pas réellement de requête (elles préparent le jeu de requête à s’exécuter au moment de l’itération), vous pouvez les utiliser sans crainte dans du code asynchrone. Pour savoir quelles méthodes peuvent continuer à être utilisées telles quelles, et quelles sont celles qui possèdent une version asynchrone, lisez la section suivante.

### `QuerySet` et méthodes de gestionnaire

Certaines méthodes de gestionnaires et de jeux de requêtes, comme `get()` et `first()`, forcent l’exécution immédiate de la requête et sont bloquantes. D’autres, comme `filter()` et `exclude()`, ne forcent pas d’exécution et peuvent donc être utilisées à partir de code asynchrone sans problème. Mais comment pouvez-vous faire cette distinction ?

Bien qu’il soit possible de faire des recherches pour savoir s’il existe une version préfixée par `a` de ces méthodes (par exemple, `aget` existe, mais pas `afilter()`), il existe une façon plus logique : examinez de quelle sorte de méthode il s’agit dans la référence de QuerySet.

Dans cette référence, vous trouverez les méthodes de `QuerySet` classées en deux catégories :

- Methods that return new querysets: These are the non-blocking ones, and don’t have asynchronous versions. You’re free to use these in any situation, but understand the constraint of no deferred queries before using `defer()`, `only()`, or `fetch_mode()`.

- Les méthodes qui ne renvoient pas de nouveaux jeux de requête: celles-ci sont bloquantes et possèdent une version asynchrone. Le nom de la version asynchrone est indiqué dans leur documentation, même si le modèle standard est d’ajouter un préfixe `a`.

Avec cette distinction, vous pouvez savoir quand vous devez utiliser les versions asynchrones et quand ce n’est pas indiqué. Par exemple, voici une requête asynchrone valable

    user = await User.objects.filter(username=my_input).afirst()

`filter()` renvoie un jeu de requête et peut donc bien être utilisée dans un environnement asynchrone, alors que `first()` est immédiatement évaluée et renvoie une instance de modèle. Ainsi, nous la modifions en `afirst()` et utilisons `await` en préambule de toute l’expression afin de l’appeler d’une manière adéquate pour l’asynchrone.

Note

Si vous oubliez le mot-clé `await`, vous pourriez voir des erreurs du genre « coroutine object has no attribute x » ou « <coroutine …> » au lieu de vos instances de modèle. Lorsque vous voyez ces erreurs, c’est qu’il manque un `await` quelque part pour transformer la coroutine en une valeur réelle.

### No deferred queries

Relations are deferred by default. If the `blog` relation is not prefetched, accessing `entry.blog` during asynchronous evaluation of a queryset attempts a blocking database query, raising `SynchronousOnlyOperation`. (A similar limitation applies to fields deferred via `defer()` or `only()`.) To avoid this, see the advice on prefetching in Objets liés.

### Transactions

Les transactions ne sont actuellement pas prises en charge pour les requêtes et mises à jour asynchrones. Si vous tentez de les utiliser quand même, vous obtiendrez des exceptions `SynchronousOnlyOperation`.

Si vous souhaitez utiliser une transaction, nous suggérons d’écrire le code ORM à l’intérieur d’une fonction synchrone séparée, puis de l’appeler en utilisant `sync_to_async` - voir Gestion du code asynchrone pour plus de détails.

## Interrogation de `JSONField`

L’implémentation des interrogations est différente pour `JSONField`, principalement en raison de l’existence des transformations de clés. Pour le montrer, nous allons utiliser le modèle d’exemple suivant

    from django.db import models

    class Dog(models.Model):
        name = models.CharField(max_length=200)
        data = models.JSONField(null=True)

        def __str__(self):
            return self.name

### Stockage et requête de `None`

As with other fields, storing `None` as the field’s value will store it as SQL `NULL`. While not recommended, it is possible to store JSON scalar `null` instead of SQL `NULL` by using the `JSONNull()` expression.

Quelle que soit la valeur stockée, lorsqu’elle est lue de la base de données, la représentation Python de la valeur scalaire `null` de JSON est la même que pour la valeur SQL `NULL`, c’est-à-dire `None`. Il peut donc être difficile de faire la distinction entre les deux.

Ceci ne s’applique qu’à `None` comme valeur de premier niveau du champ. Si `None` se trouve à l’intérieur d’une structure `list` ou `dict`, elle sera toujours interprétée comme un `null` de JSON.

When querying, `isnull=True` is used to match SQL `NULL`, while exact-matching `JSONNull()` is used to match JSON `null`.

Obsolète depuis la version 6.1: Exact-matching `None` in a query to mean JSON `null` is deprecated. After the deprecation period, it will be interpreted as SQL `NULL`.

Changed in Django 6.1:

`JSONNull()` expression was added.

    >>> from django.db.models import JSONNull
    >>> Dog.objects.create(name="Max", data=None)  # SQL NULL.
    <Dog: Max>
    >>> Dog.objects.create(name="Archie", data=JSONNull())  # JSON null.
    <Dog: Archie>
    >>> Dog.objects.filter(data=None)
    ...: RemovedInDjango70Warning: Using None as the right-hand side of an
    exact lookup on JSONField to mean JSON scalar 'null' is deprecated. Use
    JSONNull() instead (or use the __isnull lookup if you meant SQL NULL).
    ...
    <QuerySet [<Dog: Archie>]>
    >>> Dog.objects.filter(data=JSONNull())
    <QuerySet [<Dog: Archie>]>
    >>> Dog.objects.filter(data__isnull=True)
    <QuerySet [<Dog: Max>]>
    >>> Dog.objects.filter(data__isnull=False)
    <QuerySet [<Dog: Archie>]>

Sauf dans le cas où vous souhaitez vraiment travailler avec des valeurs SQL `NULL`, préférez le paramètre `null=False` et définissez une valeur par défaut convenable pour les valeurs vides, telle que `default=dict`.

Note

Le stockage de la valeur scalaire JSON `null` ne viole pas `null=False`.

Storing JSON `null` inside JSON data

While `JSONNull()` can be used in `jsonfield.key` exact lookups, it cannot be stored inside `dict` or `list` instances meant to be saved in a `JSONField`, unless a custom encoder is used. If you don’t want to use a custom encoder, use `None` instead.

### Transformations de clé, d’indice et de chemin

Pour rechercher en fonction d’une clé de dictionnaire donnée, utilisez cette clé comme nom de requête :

    >>> Dog.objects.create(
    ...     name="Rufus",
    ...     data={
    ...         "breed": "labrador",
    ...         "owner": {
    ...             "name": "Bob",
    ...             "other_pets": [
    ...                 {
    ...                     "name": "Fishy",
    ...                 }
    ...             ],
    ...         },
    ...     },
    ... )
    <Dog: Rufus>
    >>> Dog.objects.create(name="Meg", data={"breed": "collie", "owner": None})
    <Dog: Meg>
    >>> Dog.objects.filter(data__breed="collie")
    <QuerySet [<Dog: Meg>]>

To query a key for JSON `null`, `None` or `JSONNull()` can be used.

    >>> Dog.objects.filter(data__owner=None)
    <Dog: Meg>
    >>> Dog.objects.filter(data__owner=JSONNull())
    <Dog: Meg>

Il est possible d’enchaîner plusieurs clés pour former une recherche de chemin :

    >>> Dog.objects.filter(data__owner__name="Bob")
    <QuerySet [<Dog: Rufus>]>

Si la clé est un entier, elle sera interprétée comme une transformation d’indice de tableau :

    >>> Dog.objects.filter(data__owner__other_pets__0__name="Fishy")
    <QuerySet [<Dog: Rufus>]>

Si la clé est un entier négatif, elle ne peut pas être utilisée directement dans un mot-clé filtre, mais il est toujours possible d’utiliser le dépaquetage de dictionnaire pour l’utiliser dans une requête :

    >>> Dog.objects.filter(**{"data__owner__other_pets__-1__name": "Fishy"})
    <QuerySet [<Dog: Rufus>]>

MySQL, MariaDB, and Oracle < 21c

Les indices négatifs de tableaux JSON ne sont pas pris en charge.

Changed in Django 6.0:

La prise en charge des indices de tableaux JSON négatifs a été ajoutée pour SQLite.

Changed in Django 6.1:

Oracle 21c+ support for negative JSON array indices was added.

Si la clé avec laquelle vous souhaitez rechercher entre en conflit avec le nom d’une recherche existante, utilisez la recherche `contains` à la place.

Pour rechercher des clés manquantes, utilisez l’expression de recherche `isnull`:

    >>> Dog.objects.create(name="Shep", data={"breed": "collie"})
    <Dog: Shep>
    >>> Dog.objects.filter(data__owner__isnull=True)
    <QuerySet [<Dog: Shep>]>

Note

Les exemples de requêtes ci-dessus utilisent implicitement la recherche `exact`. Les transformations de clé, d’indice et de chemin peuvent aussi être chaînées avec : `icontains`, `endswith`, `iendswith`, `iexact`, `regex`, `iregex`, `startswith`, `istartswith`, `lt`, `lte`, `gt` et `gte`, tout comme avec Requêtes de contenance et de clé.

#### Expressions `KT()`

class KT(lookup)

  Représente la valeur textuelle d’une clé, d’un index ou d’une transformation de chemin de `JSONField`. On peut utiliser la notation du double soulignement dans `lookup` pour enchaîner des transformations de clés de dictionnaire ou d’index.

  Par exemple :

      >>> from django.db.models.fields.json import KT
      >>> Dog.objects.create(
      ...     name="Shep",
      ...     data={
      ...         "owner": {"name": "Bob"},
      ...         "breed": ["collie", "lhasa apso"],
      ...     },
      ... )
      <Dog: Shep>
      >>> Dog.objects.annotate(
      ...     first_breed=KT("data__breed__1"), owner_name=KT("data__owner__name")
      ... ).filter(first_breed__startswith="lhasa", owner_name="Bob")
      <QuerySet [<Dog: Shep>]>

Note

En raison de la façon dont les requêtes clé-chemin fonctionnent, `exclude()` et `filter()` ne garantissent pas la production d’ensembles exhaustifs. Si vous souhaitez inclure les objets pour lesquels le chemin n’existe pas, ajoutez l’expression `isnull`.

Avertissement

Comme n’importe quelle chaîne peut être une clé d’un objet JSON, toute recherche autre que celles mentionnées ci-dessous sera interprétée comme une recherche par clé. Aucune erreur n’est produite. Faites spécialement attention aux erreurs de frappe et vérifiez systématiquement que vos requêtes fonctionnent correctement.

Utilisateurs MariaDB et Oracle

Quand on utilise `order_by()` sur une transformation de clé, d’indice ou de chemin, le tri des objets se fait sur la représentation textuelle des valeurs. Ceci se produit parce que les bases de données MariaDB et Oracle ne fournissent pas de fonction pour convertir les valeurs JSON en leur valeur SQL équivalente.

Utilisateurs Oracle

Pour les bases de données Oracle, l’utilisation de `None` comme valeur de requête dans une requête `exclude()` renvoie des objets qui n’ont pas `null` comme valeur sur le chemin indiqué, incluant aussi les objets qui n’ont pas le chemin du tout. Avec les autres moteurs de base de données, la requête renvoie les objets où le chemin indiqué existe et la valeur n’est pas `null`.

Utilisateurs de PostgreSQL

Avec PostgreSQL, si une seule clé ou indice est donnée, l’opérateur SQL `->` est utilisé. Si plusieurs opérateurs sont donnés, alors c’est l’opérateur `#>` qui est utilisé.

Utilisateurs de SQLite

Avec SQLite, les valeurs de chaîne `"true"`, `"false"`, and `"null"` sont toujours interprétées respectivement comme `True`, `False`, et le `null` de JSON.

### Requêtes de contenance et de clé

#### `contains`

La recherche `contains` est surchargée pour les champs `JSONField`. Les objets renvoyés sont ceux pour qui le dictionnaire de paires clé-valeur est totalement contenu au premier niveau du champ. Par exemple :

    >>> Dog.objects.create(name="Rufus", data={"breed": "labrador", "owner": "Bob"})
    <Dog: Rufus>
    >>> Dog.objects.create(name="Meg", data={"breed": "collie", "owner": "Bob"})
    <Dog: Meg>
    >>> Dog.objects.create(name="Fred", data={})
    <Dog: Fred>
    >>> Dog.objects.create(
    ...     name="Merry", data={"breed": "pekingese", "tricks": ["fetch", "dance"]}
    ... )
    >>> Dog.objects.filter(data__contains={"owner": "Bob"})
    <QuerySet [<Dog: Rufus>, <Dog: Meg>]>
    >>> Dog.objects.filter(data__contains={"breed": "collie"})
    <QuerySet [<Dog: Meg>]>
    >>> Dog.objects.filter(data__contains={"tricks": ["dance"]})
    <QuerySet [<Dog: Merry>]>

Oracle et SQLite

`contains` n’est pas pris en charge avec Oracle et SQLite.

#### `contained_by`

Il s’agit de l’inverse de la recherche `contains`, les objets renvoyés sont ceux pour qui les paires de clé-valeur forment un sous-ensemble des valeurs transmises. Par exemple :

    >>> Dog.objects.create(name="Rufus", data={"breed": "labrador", "owner": "Bob"})
    <Dog: Rufus>
    >>> Dog.objects.create(name="Meg", data={"breed": "collie", "owner": "Bob"})
    <Dog: Meg>
    >>> Dog.objects.create(name="Fred", data={})
    <Dog: Fred>
    >>> Dog.objects.create(
    ...     name="Merry", data={"breed": "pekingese", "tricks": ["fetch", "dance"]}
    ... )
    >>> Dog.objects.filter(data__contained_by={"breed": "collie", "owner": "Bob"})
    <QuerySet [<Dog: Meg>, <Dog: Fred>]>
    >>> Dog.objects.filter(data__contained_by={"breed": "collie"})
    <QuerySet [<Dog: Fred>]>
    >>> Dog.objects.filter(
    ...     data__contained_by={"breed": "pekingese", "tricks": ["dance", "fetch", "hug"]}
    ... )
    <QuerySet [<Dog: Merry>, <Dog: Fred>]>

Oracle et SQLite

`contained_by` n’est pas pris en charge avec Oracle et SQLite.

#### `has_key`

Renvoie les objets où la clé donnée se trouve au premier niveau des données. Par exemple :

    >>> Dog.objects.create(name="Rufus", data={"breed": "labrador"})
    <Dog: Rufus>
    >>> Dog.objects.create(name="Meg", data={"breed": "collie", "owner": "Bob"})
    <Dog: Meg>
    >>> Dog.objects.filter(data__has_key="owner")
    <QuerySet [<Dog: Meg>]>

#### `has_keys`

Renvoie les objets où toutes les clés données se trouvent au premier niveau des données. Par exemple :

    >>> Dog.objects.create(name="Rufus", data={"breed": "labrador"})
    <Dog: Rufus>
    >>> Dog.objects.create(name="Meg", data={"breed": "collie", "owner": "Bob"})
    <Dog: Meg>
    >>> Dog.objects.filter(data__has_keys=["breed", "owner"])
    <QuerySet [<Dog: Meg>]>

#### `has_any_keys`

Renvoie les objets où au moins une des clés données se trouve au premier niveau des données. Par exemple :

    >>> Dog.objects.create(name="Rufus", data={"breed": "labrador"})
    <Dog: Rufus>
    >>> Dog.objects.create(name="Meg", data={"owner": "Bob"})
    <Dog: Meg>
    >>> Dog.objects.filter(data__has_any_keys=["owner", "breed"])
    <QuerySet [<Dog: Rufus>, <Dog: Meg>]>

## Requêtes complexes avec des objets `Q`

Keyword argument queries – in `filter()`, etc. – are « AND »ed together. If you need to execute more complex queries (for example, queries with `OR` statements), you can use Objets Q().

A Q object (`django.db.models.Q`) is an object used to encapsulate a collection of keyword arguments. These keyword arguments are specified as in « Field lookups » above.

Par exemple, cet objet `Q` représente une seule requête `LIKE`:

    from django.db.models import Q

    Q(question__startswith="What")

Les objets `Q` peuvent être combinés à l’aide des opérateurs `&`, `|` et `^`. Lorsqu’un opérateur est utilisé avec deux objets `Q`, cela produit un nouvel objet `Q`.

Par exemple, cette ligne produit un seul objet `Q` représentant la combinaison par « OR » de deux requêtes `"question__startswith"`:

    Q(question__startswith="Who") | Q(question__startswith="What")

C’est équivalent à la clause SQL `WHERE` ci-dessous :

    WHERE question LIKE 'Who%' OR question LIKE 'What%'

Vous pouvez composer des instructions de complexité arbitraire en combinant des objets `Q` avec les opérateurs `&`, `|` et `^` et en les groupant par des parenthèses. De même, les objets `Q` peuvent être inversés par l’opérateur de négation `~`, permettant à des requêtes combinées d’utiliser à la fois des requêtes normales et des requêtes inversées (`NOT`) :

    Q(question__startswith="Who") | ~Q(pub_date__year=2005)

Chaque fonction de recherche acceptant des paramètres nommés (par ex. `filter()`, `exclude()`, `get()`) peut aussi recevoir un ou plusieurs objets `Q` comme paramètre positionnel (non nommé). Si vous indiquez plusieurs objets `Q` comme paramètres d’une fonction de recherche, les paramètres seront combinés avec « AND ». Par exemple :

    Poll.objects.get(
        Q(question__startswith="Who"),
        Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)),
    )

… se traduit grossièrement en SQL par :

    SELECT * from polls WHERE question LIKE 'Who%'
        AND (pub_date = '2005-05-02' OR pub_date = '2005-05-06')

Les fonctions de recherche peuvent mélanger l’utilisation d’objets `Q` et de paramètres nommés. Tous les paramètres fournis à une fonction de recherche (que ce soit des paramètres nommés ou des objets `Q`) sont combinés par l’opérateur « AND ». Cependant, si un objet `Q` est fourni, il doit précéder toute définition de paramètre nommé. Par exemple :

    Poll.objects.get(
        Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)),
        question__startswith="Who",
    )

… correspond à une requête valable, équivalente à l’exemple précédent ; mais :

    # INVALID QUERY
    Poll.objects.get(
        question__startswith="Who",
        Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)),
    )

… n’est pas correct.

Voir aussi

Les exemples de requêtes OR dans les tests unitaires de Django montrent quelques utilisations possibles de `Q`.

## Comparaison d’objets

Pour comparer deux instances de modèle, utilisez l’opérateur de comparaison standard de Python, le double signe égal : `==`. En arrière-plan, ce sont les valeurs clés primaires des deux modèles qui sont comparées.

En utilisant l’exemple `Entry` ci-dessus, les deux instructions suivantes sont équivalentes :

    >>> some_entry == other_entry
    >>> some_entry.id == other_entry.id

Si la clé primaire d’un modèle ne s’appelle pas `id`, aucun problème. Les comparaisons utilisent toujours la clé primaire, quel que soit son nom. Par exemple, si un champ clé primaire d’un modèle s’appelle `name`, ces deux lignes sont équivalentes :

    >>> some_obj == other_obj
    >>> some_obj.name == other_obj.name

## Suppression d’objets

La méthode de suppression se nomme `delete()`. Cette méthode supprime immédiatement l’objet et renvoie le nombre d’objets supprimés ainsi qu’un dictionnaire avec le nombre de suppressions par type d’objet. Exemple :

    >>> e.delete()
    (1, {'blog.Entry': 1})

Il est aussi possible de supprimer des objets groupés. Chaque `QuerySet` comporte une méthode `delete()` qui supprime tous les objets contenus dans l’objet `QuerySet`.

Par exemple, cette commande supprime tous les objets `Entry` dont l’année de `pub_date` est 2005 :

    >>> Entry.objects.filter(pub_date__year=2005).delete()
    (5, {'webapp.Entry': 5})

Sachez toutefois que cette opération sera autant que possible exécutée purement au niveau SQL, ce qui signifie que les méthodes `delete()` des instances individuelles ne seront pas forcément appelées durant le processus. Si vous avez écrit une méthode `delete()` personnalisée dans une classe de modèle et que vous voulez être certain qu’elle soit appelée, vous devrez supprimer « manuellement » les instances de ce modèle (par ex. en itérant sur un objet `QuerySet` et en appelant explicitement `delete()` sur chaque instance) plutôt que d’employer la méthode de suppression groupée `delete()` de l’objet `QuerySet`.

Lorsque Django supprime un objet, il émule par défaut le comportement de la contrainte SQL `ON DELETE CASCADE`. En d’autres termes, tout objet possédant des clés étrangères vers l’objet en cours de suppression seront également supprimés. Par exemple :

    b = Blog.objects.get(pk=1)
    # This will delete the Blog and all of its Entry objects.
    b.delete()

Ce comportement en cascade peut être personnalisé au moyen du paramètre `on_delete` de la classe `ForeignKey`.

Notez que `delete()` est la seule méthode de `QuerySet` qui n’est pas exposée sur un objet `Manager`. Il s’agit d’un mécanisme de sécurité pour vous empêcher d’exécuter accidentellement `Entry.objects.delete()` ce qui supprimerait toutes les lignes. Si vous souhaitez vraiment supprimer tous les objets, vous devez alors indiquer explicitement une requête contenant tous les objets :

    Entry.objects.all().delete()

## Copie des instances de modèles

Même s’il n’existe pas de méthode intégrée pour la copie d’instances de modèles, il est possible de créer facilement de nouvelles instances en copiant toutes les valeurs des champs d’une autre instance. Dans le cas le plus simple, on peut définir `pk` à `None` et `_state.adding` to `True`. En utilisant notre exemple de blogue :

    blog = Blog(name="My blog", tagline="Blogging is easy")
    blog.save()  # blog.pk == 1

    blog.pk = None
    blog._state.adding = True
    blog.save()  # blog.pk == 2

Les choses se compliquent lorsqu’il y a de l’héritage. Considérons une sous-classe de `Blog`:

    class ThemeBlog(Blog):
        theme = models.CharField(max_length=200)

    django_blog = ThemeBlog(name="Django", tagline="Django is easy", theme="python")
    django_blog.save()  # django_blog.pk == 3

En raison du fonctionnement de l’héritage, vous devez définir à `None` à la fois `pk` et `id`, ainsi que `_state.adding` à `True`:

    django_blog.pk = None
    django_blog.id = None
    django_blog._state.adding = True
    django_blog.save()  # django_blog.pk == 4

Ce processus ne copie pas les relations ne faisant pas partie de la table de base de données du modèle. Par exemple, `Entry` possède un champ `ManyToManyField` vers `Author`. Après avoir dupliqué un objet « entry », vous devez définir les relations plusieurs-à-plusieurs vers le nouvel objet :

    entry = Entry.objects.all()[0]  # some previous entry
    old_authors = entry.authors.all()
    entry.pk = None
    entry._state.adding = True
    entry.save()
    entry.authors.set(old_authors)

Pour un champ `OneToOneField`, vous devez dupliquer l’objet lié et l’attribuer au champ correspondant du nouvel objet pour éviter la rupture de la contrainte unique du champ un-à-un. Par exemple, en supposant que `entry` a déjà été dupliqué comme ci-dessus :

    detail = EntryDetail.objects.all()[0]
    detail.pk = None
    detail._state.adding = True
    detail.entry = entry
    detail.save()

Notez qu’il n’est pas possible de copier des instances de modèles avec des champs différés en utilisant ce principe tant que des valeurs ne leur sont pas attribuées :

    >>> blog = Blog.objects.defer("name")[0]
    >>> blog.pk = None
    >>> blog._state.adding = True
    >>> blog.save()
    Traceback (most recent call last):
        ...
    AttributeError: Cannot retrieve deferred field 'name' from an unsaved model.
    >>> blog.name = "Another Blog"
    >>> blog.save()

## Mise à jour simultanée de plusieurs objets

Il peut arriver que vous souhaitiez définir la valeur d’un certain champ pour tous les objets d’un `QuerySet`. C’est faisable à l’aide de la méthode `update()`. Par exemple :

    # Update all the headlines with pub_date in 2007.
    Entry.objects.filter(pub_date__year=2007).update(headline="Everything is the same")

Seuls les champs qui ne sont pas des relations et les champs `ForeignKey` peuvent être mis à jour avec cette méthode. Pour mettre à jour un champ qui ne représente pas une relation, indiquez la nouvelle valeur sous forme de constante. Pour mettre à jour un champ `ForeignKey`, indiquez la nouvelle valeur sous forme d’une instance de modèle vers laquelle le champ devra pointer. Par exemple :

    >>> b = Blog.objects.get(pk=1)

    # Change every Entry so that it belongs to this Blog.
    >>> Entry.objects.update(blog=b)

La méthode `update()` est appliquée immédiatement et renvoie le nombre de lignes correspondant à la requête (ce qui ne correspond pas toujours au nombre de lignes mises à jour si certaines lignes ont déjà la bonne valeur). La seule restriction sur l’objet `QuerySet` mis à jour est qu’il ne peut accéder qu’à une seule table de base de données, la table principale du modèle. Vous pouvez filtrer selon des champs liés, mais vous ne pouvez mettre à jour des colonnes que dans la table principale du modèle. Exemple :

    >>> b = Blog.objects.get(pk=1)

    # Update all the headlines belonging to this Blog.
    >>> Entry.objects.filter(blog=b).update(headline="Everything is the same")

Soyez conscient que la méthode `update()` est directement convertie en instruction SQL. Il s’agit d’une opération groupée pour des mises à jour directes. Elle ne fait appel à aucune méthode `save()` des modèles, n’émet aucun signal `pre_save` ou `post_save` (qui sont des conséquences de l’appel à `save()`) et ne respecte pas l’option de champ `auto_now`. Si vous souhaitez enregistrer chaque élément d’un objet `QuerySet` tout en garantissant que la méthode `save()` de chaque instance est appelée, vous n’avez pas besoin d’une fonction particulière pour cela. Effectuez une boucle et appelez `save()` pour chaque objet :

    for item in my_queryset:
        item.save()

Calls to update can also use Expressions F() to update one field based on the value of another field in the model. This is especially useful for incrementing counters based upon their current value. For example, to increment the pingback count for every entry in the blog:

    >>> Entry.objects.update(number_of_pingbacks=F("number_of_pingbacks") + 1)

Cependant, au contraire des objets `F()` dans les clauses `filter()` et `exclude()`, il n’est pas autorisé d’introduire des jointures lors de l’utilisation de `F()` dans des mises à jour ; seuls des champs du modèle en cours de mise à jour peuvent être référencés. Si vous essayez d’introduire une jointure avec un objet `F()`, une erreur `FieldError` sera générée :

    # This will raise a FieldError
    >>> Entry.objects.update(headline=F("blog__name"))

## Objets liés

When you define a relationship in a model (with `ForeignKey`, `OneToOneField`, or `ManyToManyField`), instances of the model class gain accessor attributes for the related object(s).

Using the models at the top of this page, for example, an `Entry` object `e` has its associated `Blog` object accessible in its `blog` attribute: `e.blog`.

(En arrière-plan, cette fonctionnalité est implémentée par des descripteurs Python. Cela n’a pas de conséquence particulière, mais nous le mentionnons pour les curieux).

Django also creates API accessors for the « other » side of the relationship – the link from the related model to the model that defines the relationship. For example, a `Blog` object `b` has a manager that returns all related `Entry` objects in the `entry_set` attribute: `b.entry_set.all()`.

These accessors may be prefetched by the `QuerySet` methods `select_related()` or `prefetch_related()`. If not prefetched, access will trigger an on-demand fetch through the model’s fetch mode.

Tous les exemples de cette section utilisent les modèles d’exemple `Blog`, `Author` et `Entry` définis au sommet de cette page.

### Relations un-à-plusieurs

#### Sens « descendant »

Si un modèle possède un champ `ForeignKey`, les instances de ce modèle ont accès à l’objet lié par un attribut du modèle.

Exemple :

    >>> e = Entry.objects.get(id=2)
    >>> e.blog  # Returns the related Blog object.

Un attribut d’objet lié (clé étrangère) est accessible en lecture et en écriture. Comme l’on peut s’y attendre, les modifications de la clé étrangère ne sont pas enregistrées en base de données tant que `save()` n’a pas été appelée. Exemple :

    >>> e = Entry.objects.get(id=2)
    >>> e.blog = some_blog
    >>> e.save()

Si un champ `ForeignKey` possède `null=True` (c’est-à-dire qu’il autorise les valeurs `NULL`), vous pouvez lui attribuer `None` pour supprimer la relation. Exemple :

    >>> e = Entry.objects.get(id=2)
    >>> e.blog = None
    >>> e.save()  # "UPDATE blog_entry SET blog_id = NULL ...;"

L’accès « descendant » aux relations un-à-plusieurs est mis en cache lors du premier accès à l’objet lié. Les accès suivants à la clé étrangère depuis la même instance d’objet se font à partir du cache. Exemple :

    >>> e = Entry.objects.get(id=2)
    >>> print(e.blog)  # Hits the database to retrieve the associated Blog.
    >>> print(e.blog)  # Doesn't hit the database; uses cached version.

Notez que la méthode `select_related()` de `QuerySet` peuple de manière récursive et anticipée le cache de toutes les relations un-à-plusieurs. Exemple :

    >>> e = Entry.objects.select_related().get(id=2)
    >>> print(e.blog)  # Doesn't hit the database; uses cached version.
    >>> print(e.blog)  # Doesn't hit the database; uses cached version.

#### Accès aux relations inverses

Si un modèle possède un champ `ForeignKey`, les instances du modèle de clé étrangère auront accès à un `Manager` renvoyant toutes les instances du premier modèle. Par défaut, ce `Manager` se nomme `XXX_set`, où `XXX` est le nom du modèle source en minuscules. Ce `Manager` renvoie des instances `QuerySet`, qui peuvent être filtrées et manipulées comme le décrit la section « Sélection d’objets » ci-dessus.

Exemple :

    >>> b = Blog.objects.get(id=1)
    >>> b.entry_set.all()  # Returns all Entry objects related to Blog.

    # b.entry_set is a Manager that returns QuerySets.
    >>> b.entry_set.filter(headline__contains="Lennon")
    >>> b.entry_set.count()

Il est possible de surcharger le nom `XXX_set` en définissant le paramètre `related_name` dans la définition du champ `ForeignKey`. Par exemple, si le modèle `Entry` était modifié en définissant `blog = ForeignKey(Blog, on_delete=models.CASCADE, related_name='entries')`, l’exemple ci-dessus aurait été écrit comme ceci :

    >>> b = Blog.objects.get(id=1)
    >>> b.entries.all()  # Returns all Entry objects related to Blog.

    # b.entries is a Manager that returns ``QuerySet`` instances.
    >>> b.entries.filter(headline__contains="Lennon")
    >>> b.entries.count()

#### Utilisation d’un gestionnaire inverse personnalisé

Par défaut, le gestionnaire `RelatedManager` utilisé pour les relations inverses est une sous-classe du gestionnaire par défaut du modèle. Si vous aimeriez indiquer un autre gestionnaire pour une requête précise, vous pouvez utiliser la syntaxe suivante :

    from django.db import models

    class Entry(models.Model):
        # ...
        objects = models.Manager()  # Default Manager
        entries = EntryManager()  # Custom Manager

    b = Blog.objects.get(id=1)
    b.entry_set(manager="entries").all()

Si `EntryManager` effectuait du filtrage par défaut dans sa méthode `get_queryset()`, ce filtrage s’appliquerait à la méthode `all()`.

En indiquant un gestionnaire inverse personnalisé, cela permet aussi d’appeler ses méthodes personnalisées :

    b.entry_set(manager="entries").is_published()

Interaction avec le préchargement (prefetch)

Lors de l’appel à `prefetch_related()` avec une relation inverse, le gestionnaire par défaut sera utilisé. Si vous souhaitez précharger des objets liés en utilisant un gestionnaire inverse personnalisé, utilisez `Prefetch()`. Par exemple

    from django.db.models import Prefetch

    prefetch_manager = Prefetch("entry_set", queryset=Entry.entries.all())
    Blog.objects.prefetch_related(prefetch_manager)

#### Méthodes supplémentaires pour manipuler des objets liés

En plus des méthodes `QuerySet` présentées ci-dessus dans « Sélection d’objets », l’objet `Manager` d’une `ForeignKey` possède des méthodes supplémentaires pour gérer l’ensemble des objets liés. Vous trouverez ci-dessous un résumé de ces méthodes et des détails plus complets peuvent être lus dans la référence des objets liés.

`add(obj1, obj2, ...)`

  Ajoute les objets modèles indiqués à l’ensemble des objets liés.

`create(**kwargs)`

  Crée un nouvel objet, l’enregistre et le place dans l’ensemble des objets liés. Renvoie l’objet nouvellement créé.

`remove(obj1, obj2, ...)`

  Enlève les objets modèles indiqués de l’ensemble des objets liés.

`clear()`

  Enlève tous les objets de l’ensemble des objets liés.

`set(objs)`

  Remplace l’ensemble des objets liés.

Pour définir les membres d’un ensemble d’objets liés, utilisez la méthode `set()` avec une liste d’instances d’objets. Par exemple, si `e1` et `e2` sont des instances `Entry`:

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

Si la méthode `clear()` est disponible, tout objet pré-existant sera enlevé de `entry_set` avant que tous les objets de la séquence itérable (dans ce cas, une liste) soient ajoutés à l’ensemble. Si la méthode `clear()` n’est pas disponible, tous les objets de la séquence itérable sont ajoutés sans que les éléments existants ne soient enlevés.

Toute opération « inverse » décrite dans cette section prend immédiatement effet au niveau de la base de données. Tout ajout, création ou suppression est immédiatement et automatiquement enregistré dans la base de données.

### Relations plusieurs-à-plusieurs

Les deux côtés d’une relation plusieurs-à-plusieurs obtiennent automatiquement l’API d’accès à l’autre côté. L’API fonctionne de manière identique à la relation « inverse » d’une relation un-à-plusieurs décrite ci-dessus.

Une des différences tient dans le nommage de l’attribut : le modèle qui définit le champ `ManyToManyField` utilise le nom d’attribut du champ lui-même, alors que le modèle « inverse » utilise le nom de modèle en minuscules du modèle original en y ajoutant `'_set'` (tout comme pour les relations un-à-plusieurs inverses).

Un exemple va rendre tout ceci plus compréhensible :

    e = Entry.objects.get(id=3)
    e.authors.all()  # Returns all Author objects for this Entry.
    e.authors.count()
    e.authors.filter(name__contains="John")

    a = Author.objects.get(id=5)
    a.entry_set.all()  # Returns all Entry objects for this Author.

Tout comme pour `ForeignKey`, il est possible d’indiquer une valeur `related_name` dans `ManyToManyField`. Dans l’exemple ci-dessus, si le champ `ManyToManyField` de `Entry` était défini avec `related_name='entries'`, chaque instance de `Author` posséderait un attribut `entries` au lieu de `entry_set`.

Une autre différence par rapport aux relations un-à-plusieurs est qu’en plus des instances de modèles, les méthodes `add()`, `set()` et `remove()` des relations plusieurs-à-plusieurs acceptent des valeurs de clé primaire. Par exemple, si `e1` et `e2` sont des instances `Entry`, alors ces appels à `set()` fonctionnent de manière identique

    a = Author.objects.get(id=5)
    a.entry_set.set([e1, e2])
    a.entry_set.set([e1.pk, e2.pk])

#### Filtrage des relations plusieurs-à-plusieurs

Lors de l’appel à `filter()` sur une relation plusieurs-à-plusieurs, il faut savoir que la jointure entre `Entry` et le modèle intermédiaire vers `Author` est effectuée une seule fois, résultant en un filtre restrictif, ou « collant ». Considérez l’exemple suivant :

    >>> from datetime import date
    >>> batucada = Blog.objects.create(name="Batucada Blog")
    >>> e = Entry.objects.create(
    ...     blog=batucada,
    ...     headline="Supporting social movements with drums",
    ...     pub_date=date(2019, 6, 14),
    ... )

    >>> gloria = Author.objects.create(name="Gloria")
    >>> anna = Author.objects.create(name="Anna")
    >>> e.authors.add(gloria, anna)

    >>> anna.entry_set.filter(authors__name="Gloria")
    <QuerySet []>

Cette requête filtrée recherche des entrées de blog dont les auteurs sont `anna` et `gloria`. Vous auriez espéré qu’elle renvoie l’entrée `e`. Cependant, la condition du filtre, qui traverse la relation plusieurs-à-plusieurs entre `Entry` et `Author`, produit un `QuerySet` vide.

Comme la jointure entre `Entry` et le modèle intermédiaire vers `Author` est unique, aucun objet des modèles joints, c’est-à-dire une relation entre un auteur et une entrée, ne peut remplir la condition de requête (des entrées dont l’auteur est à la fois `anna` et `gloria`). Vous pouvez contourner ce comportement en enchaînant deux appels `filter()` consécutifs, ce qui aboutira à deux jointures séparées et donc à un filtrage plus permissif :

    >>> anna.entry_set.filter().filter(authors__name="Gloria")
    <QuerySet [<Entry: Supporting social movements with drums>]>

exclude() est également collant

Veuillez noter que dans cet exemple, `exclude()` se comporte de la même façon que `filter()` même si son implémentation est différente. Lors du passage à travers la relation plusieurs-à-plusieurs, l’entrée `e` n’est pas exclue, alors même que Gloria est co-autrice :

    >>> anna.entry_set.exclude(authors__name="Gloria")
    <QuerySet [<Entry: Supporting social movements with drums>]>

Lorsque l’on enchaîne un second appel à `exclude()`, un `QuerySet` vide est renvoyé, comme attendu :

    >>> anna.entry_set.exclude().exclude(authors__name="Gloria")
    <QuerySet []>

Cependant, dans d’autres situations, `exclude()` se comporte différemment de `filter()`. Lisez la note dans la section « Traversée des relations multivaluées » ci-dessus.

### Relations un-à-un

Les relations un-à-un sont très semblables aux relations plusieurs-à-un. Si vous définissez un champ `OneToOneField` dans votre modèle, les instances de ce modèle auront accès à l’objet lié via un attribut du modèle.

Par exemple :

    class EntryDetail(models.Model):
        entry = models.OneToOneField(Entry, on_delete=models.CASCADE)
        details = models.TextField()

    ed = EntryDetail.objects.get(id=2)
    ed.entry  # Returns the related Entry object.

La différence survient dans les requêtes « inverses ». Le modèle lié dans une relation un-à-un a aussi accès à un objet `Manager`, mais celui-ci représente un objet unique et non pas une collection d’objets :

    e = Entry.objects.get(id=2)
    e.entrydetail  # returns the related EntryDetail object

Si aucun objet n’a été attribué à cette relation, Django génère une exception `DoesNotExist`.

Il est possible d’attribuer des instances à la relation inverse sur le même principe que pour les relations descendantes :

    e.entrydetail = ed

### Comment la relation inverse est-elle possible ?

D’autres interfaces d’abstraction objet-relationnel exigent de définir les relations des deux côtés. Les développeurs Django pensent qu’il s’agit d’une violation du principe DRY (Don’t Repeat Yourself, ne pas se répéter), c’est pourquoi Django ne demande de définir la relation que d’un seul côté.

Mais comment est-ce possible, dans la mesure où une classe de modèle ne sait pas quelles autres classes de modèles lui sont liées avant que ces autres classes soient elles-mêmes chargées ?

The answer lies in the `app registry`. When Django starts, it imports each application listed in `INSTALLED_APPS`, and then the `models` module inside each application. Whenever a new model class is created, Django adds backward-relationships to any related models. If the related models haven’t been imported yet, Django keeps track of the relationships and adds them when the related models eventually are imported.

C’est pour cette raison qu’il est particulièrement important que tous les modèles utilisés soient définis dans des applications qui figurent dans `INSTALLED_APPS`. Sinon, les relations inverses pourraient ne pas fonctionner correctement.

### Les requêtes sur les objets liés

Les requêtes impliquant des objets liés suivent les mêmes règles que les requêtes contenant des valeurs de champs normales. Pour indiquer la valeur à laquelle doit correspondre une requête, vous pouvez utiliser soit une instance de l’objet lui-même, soit la valeur de clé primaire de l’objet.

Par exemple, si vous avez un objet `Blog` `b` avec `id=5`, les trois requêtes suivantes sont identiques :

    Entry.objects.filter(blog=b)  # Query using object instance
    Entry.objects.filter(blog=b.id)  # Query using id from instance
    Entry.objects.filter(blog=5)  # Query using id directly

## Recours au code SQL brut

Si vous rencontrez le besoin de devoir écrire une requête SQL trop complexe pour l’abstraction de base de données de Django, il peut être nécessaire d’écrire le code SQL à la main. Django offre quelques options pour l’écriture de requêtes SQL brutes ; voir Lancement de requêtes SQL brutes.

Finalement, il est important de noter que la couche de base de données de Django n’est qu’une interface vers votre base de données. Vous pouvez accéder à votre base de données par d’autres outils, langages de programmation ou environnements de base de données ; il n’y a rien de spécifique à Django en ce qui concerne la base de données.