# Documentation Django 6.0.7

# Expressions de requête

Les expressions de requête décrivent une valeur ou un calcul pouvant faire partie d’une mise à jour, d’une création, d’un filtre, d’un tri, d’une annotation ou d’une agrégation. Lorsque le résultat d’une expression est une valeur booléenne, elle peut être utilisée directement dans un filtre. Il existe un certain nombre d’expressions intégrées (documentées ci-dessous) pouvant aider à construire des requêtes. Les expressions peuvent être combinées, ou même imbriquées dans certains cas, afin de former des calculs plus complexes.

## Arithmétique prise en charge

Django prend en charge la négation, l’addition, la soustraction, la multiplication, la division, l’arithmétique modulo ainsi que l’opérateur de puissance pour les expressions de requête, à l’aide de constantes Python, de variables et même d’autres expressions.

## Champ de sortie

De nombreuses expressions documentées dans cette section acceptent un paramètre facultatif `output_field`. Si présent, Django chargera la valeur dans ce type de champ après l’avoir récupérée de la base de données.

`output_field` accepte une instance de champ de modèle, telle que `IntegerField()` ou `BooleanField()`. En général, le champ n’a pas besoin de paramètres tels que `max_length`, car ces paramètres sont liés à la validation des données qui ne sera pas appliquée sur la valeur de sortie de l’expression.

`output_field` n’est obligatoire que lorsque Django est incapable de déterminer automatiquement le type de champ du résultat, comme pour des expressions complexes qui mélangent les types de champs. Par exemple, l’addition d’un `DecimalField()` et d’un `FloatField()` exige la définition de `output_field`, comme `output_field=FloatField()`.

`output_field` permet aussi d’utiliser des champs personnalisés qui effectuent des conversions de type en dehors d’un contexte spécifique d’un champ de modèle. Par exemple, si vous avez besoin d’effectuer fréquemment des calculs de date avec `timedelta`, vous pouvez créer un champ personnalisé qui s’occupe de la conversion, garantissant des résultats cohérents quelle que soit la base de données. Voir Écriture de champs de modèles personnalisés.

## Quelques exemples

    >>> from django.db.models import Count, F, Value
    >>> from django.db.models.functions import Length, Upper
    >>> from django.db.models.lookups import GreaterThan

    # Find companies that have more employees than chairs.
    >>> Company.objects.filter(num_employees__gt=F("num_chairs"))

    # Find companies that have at least twice as many employees
    # as chairs. Both the querysets below are equivalent.
    >>> Company.objects.filter(num_employees__gt=F("num_chairs") * 2)
    >>> Company.objects.filter(num_employees__gt=F("num_chairs") + F("num_chairs"))

    # How many chairs are needed for each company to seat all employees?
    >>> company = (
    ...     Company.objects.filter(num_employees__gt=F("num_chairs"))
    ...     .annotate(chairs_needed=F("num_employees") - F("num_chairs"))
    ...     .first()
    ... )
    >>> company.num_employees
    120
    >>> company.num_chairs
    50
    >>> company.chairs_needed
    70

    # Create a new company using expressions.
    >>> company = Company.objects.create(name="Google", ticker=Upper(Value("goog")))
    >>> company.ticker
    'GOOG'

    # Annotate models with an aggregated value. Both forms
    # below are equivalent.
    >>> Company.objects.annotate(num_products=Count("products"))
    >>> Company.objects.annotate(num_products=Count(F("products")))

    # Aggregates can contain complex computations also
    >>> Company.objects.annotate(num_offerings=Count(F("products") + F("services")))

    # Expressions can also be used in order_by(), either directly
    >>> Company.objects.order_by(Length("name").asc())
    >>> Company.objects.order_by(Length("name").desc())
    # or using the double underscore lookup syntax.
    >>> from django.db.models import CharField
    >>> from django.db.models.functions import Length
    >>> CharField.register_lookup(Length)
    >>> Company.objects.order_by("name__length")

    # Boolean expression can be used directly in filters.
    >>> from django.db.models import Exists, OuterRef
    >>> Company.objects.filter(
    ...     Exists(Employee.objects.filter(company=OuterRef("pk"), salary__gt=10))
    ... )

    # Lookup expressions can also be used directly in filters
    >>> Company.objects.filter(GreaterThan(F("num_employees"), F("num_chairs")))
    # or annotations.
    >>> Company.objects.annotate(
    ...     need_chairs=GreaterThan(F("num_employees"), F("num_chairs")),
    ... )

## Expressions intégrées

Note

Ces expressions sont définies dans `django.db.models.expressions` et `django.db.models.aggregates`, mais par commodité elles sont disponibles et habituellement importées à partir de `django.db.models`.

### Expressions `F()`

class F

Un objet `F()` représente la valeur d’un champ de modèle, la valeur transformée d’un champ de modèle ou une colonne annotée. Il permet de se référer à des valeurs de champs de modèles et d’effectuer avec elles des opérations en base de données sans avoir à les récupérer préalablement de la base de données vers la mémoire Python.

Au lieu de cela, Django utilise un objet `F()` pour générer une expression SQL qui décrit l’opération requise au niveau de la base de données.

Essayons de voir cela avec un exemple. Normalement, on peut faire quelque chose comme ceci :

    # Tintin filed a news story!
    reporter = Reporters.objects.get(name="Tintin")
    reporter.stories_filed += 1
    reporter.save()

Ici, nous avons obtenu en mémoire la valeur de `reporter.stories_filed` à partir de la base de données et nous l’avons manipulée avec des opérateurs Python familiers. Nous avons ensuite enregistré l’objet modifié en base de données. Cependant, nous aurions tout aussi bien pu écrire :

    from django.db.models import F

    reporter = Reporters.objects.get(name="Tintin")
    reporter.stories_filed = F("stories_filed") + 1
    reporter.save()

Même si `reporter.stories_filed = F('stories_filed') + 1` apparaît comme une attribution normale de valeur à un attribut d’instance en Python, il s’agit en réalité d’une construction SQL décrivant une opération en base de données.

Lorsque Django rencontre une instance de `F()`, il surcharge les opérateurs Python standards pour créer une expression SQL encapsulée ; dans ce cas, une instruction qui demande à la base de données d’incrémenter le champ de base de données représenté par `reporter.stories_filed`.

Quelle que soit la valeur que contenait `reporter.stories_filed`, Python n’a jamais besoin de la connaître, car tout se passe en base de données. Tout ce que Python fait à travers la classe `F()` de Django est de créer la syntaxe SQL pour se référer au champ et décrire l’opération.

En plus d’être utilisée dans les opérations sur des instances uniques comme ci-dessus, `F()` peut être utilisée avec `update()` pour des mises à jour en vrac sur des jeux `QuerySet`. Cela permet de réduire les deux requêtes utilisées ci-dessus, `get()` et `save()`, à une seule :

    reporter = Reporters.objects.filter(name="Tintin")
    reporter.update(stories_filed=F("stories_filed") + 1)

Nous pouvons aussi utiliser `update()` pour incrémenter la valeur du champ sur plusieurs objets, ce qui peut être beaucoup plus rapide que de récupérer toutes les valeurs en Python depuis la base de données, de les passer en boucle en incrémentant la valeur des champs pour chacun, et de les réenregistrer individuellement dans la base de données :

    Reporter.objects.update(stories_filed=F("stories_filed") + 1)

`F()` peut donc offrir des avantages de performance en :

- faisant travailler la base de données plutôt que le code Python ;

- réduisant le nombre de requêtes de certaines opérations.

#### Segmentation des expressions `F()`

Pour les champs basés sur des chaînes, du texte ou sur `ArrayField`, il est possible d’utiliser la syntaxe Python de segmentation de tableau. Les indices commencent à 0. L’argument `step` de `slice` ainsi que l’indiçage négatif ne sont pas pris en charge. Par exemple :

    >>> # Replacing a name with a substring of itself.
    >>> writer = Writers.objects.get(name="Priyansh")
    >>> writer.name = F("name")[1:5]
    >>> writer.save()
    >>> writer.name
    'riya'

#### Prévention des conflits de concurrence avec `F()`

Un autre bénéfice notable de `F()` est que de faire mettre à jour la valeur d’un champ par la base de données plutôt qu’en Python évite un conflit de concurrence.

Si deux fils d’exécution Python exécutent le code du premier exemple ci-dessus, un des fils pourrait obtenir, incrémenter et enregistrer une valeur de champ après que l’autre fil l’ait obtenu de la base de données. La valeur enregistrée par le second fil d’exécution sera basé sur la valeur d’origine ; le travail du premier fil d’exécution sera tout simplement perdu.

Si c’est la base de données qui est responsable de la mise à jour du champ, le processus est plus robuste : la valeur du champ sera toujours mise à jour en fonction de sa valeur en base de données au moment même de l’appel à `save()` ou `update()`, et non pas en fonction de la valeur obtenue au moment de la création de l’instance de modèle.

#### Les attributions par `F()` sont actualisées après `Model.save()`

Les objets `F()` attribués aux champs de modèles sont actualisés à partir de la base de données lors de l’enregistrement `save()` pour les moteurs qui le supportent sans impliquer de requête supplémentaire (SQLite, PostgreSQL et Oracle), sinon en tant que champ différé (MySQL ou MariaDB). Par exemple :

    >>> reporter = Reporters.objects.get(name="Tintin")
    >>> reporter.stories_filed = F("stories_filed") + 1
    >>> reporter.save()
    >>> reporter.stories_filed  # This triggers a refresh query on MySQL/MariaDB.
    14  # Assuming the database value was 13 when the object was saved.

Changed in Django 6.0:

Dans les versions précédentes de Django, les objets `F()` n’étaient pas actualisés depuis la base de données lors de `save()`, ce qui faisait qu’ils étaient évalués et conservés à chaque enregistrement d’une instance.

#### Utilisation de `F()` dans les filtres

`F()` est aussi très utile dans les filtres `QuerySet`, là où il est possible de filtrer un jeu d’objets en fonction de critères basés sur les valeurs des champs, plutôt que sur des valeurs Python.

Cette utilisation est documentée dans Expressions F() dans les requêtes.

#### Utilisation de `F()` avec les annotations

`F()` peut être utilisée pour créer des champs dynamiques pour les modèles en combinant plusieurs champs de manière arithmétique :

    company = Company.objects.annotate(chairs_needed=F("num_employees") - F("num_chairs"))

Si les champs que vous combinez sont de types différents, il est nécessaire d’indiquer à Django le type de champ qui en résultera. La plupart des expressions prennent en charge output_field pour ce genre de situation, mais comme ce n’est pas le cas avec `F()`, il est nécessaire d’envelopper l’expression dans `ExpressionWrapper`:

    from django.db.models import DateTimeField, ExpressionWrapper, F

    Ticket.objects.annotate(
        expires=ExpressionWrapper(
            F("active_at") + F("duration"), output_field=DateTimeField()
        )
    )

Lorsqu’elle fait référence à des champs relationnels tels qu’une clé `ForeignKey`, `F()` renvoie la valeur de clé primaire plutôt qu’une instance de modèle :

    >>> car = Company.objects.annotate(built_by=F("manufacturer"))[0]
    >>> car.manufacturer
    <Manufacturer: Toyota>
    >>> car.built_by
    3

#### Utilisation de `F()` pour trier les valeurs nulles

Utilisez `F()` et les paramètres nommés `nulls_first` ou `nulls_last` des expressions `Expression.asc()` ou `desc()` pour contrôler le tri des valeurs nulles d’un champ. Par défaut, l’ordre dépend de la base de données.

Par exemple, pour trier les entreprises qui n’ont pas été contactées (`last_contacted` est nul) après les entreprises qui ont été contactées

    from django.db.models import F

    Company.objects.order_by(F("last_contacted").desc(nulls_last=True))

#### Utilisation de `F()` avec les opérations logiques

Les expressions `F()` produisant des valeurs booléennes peuvent être logiquement inversées avec l’opérateur d’inversion `~F()`. Par exemple, pour inverser l’état d’activation des entreprises

    from django.db.models import F

    Company.objects.update(is_active=~F("is_active"))

### Expressions `Func()`

Les expressions `Func()` sont le type de base de toutes les expressions qui impliquent des fonctions de base de données telles que `COALESCE` et `LOWER`, ou des agrégations comme `SUM`. Elles peuvent être utilisées directement :

    from django.db.models import F, Func

    queryset.annotate(field_lower=Func(F("field"), function="LOWER"))

ou elles peuvent être utilisées pour bâtir une bibliothèque de fonctions de base de données :

    class Lower(Func):
        function = "LOWER"

    queryset.annotate(field_lower=Lower("field"))

Mais dans les deux cas, chaque modèle du jeu de requête résultant sera annoté avec un attribut supplémentaire `field_lower` dont le contenu correspondra plus ou moins au code SQL suivant :

    SELECT
        ...
        LOWER("db_table"."field") as "field_lower"

Voir Fonctions de base de données pour une liste de fonctions de base de données intégrées.

L’API `Func` se présente comme suit :

class Func(*expressions, **extra)
  function

    Un attribut de classe décrivant la fonction qui sera générée. Plus précisément, `function` sera inséré par le substituant `function` à l’intérieur de `template`. Sa valeur par défaut est `None`.

  template

    Un attribut de classe, sous forme de chaîne de format, qui décrit le code SQL généré pour cette fonction. Sa valeur par défaut est `'%(function)s(%(expressions)s)'`.

    SI vous construisez du code SQL tel que `strftime('%W', 'date')` et que vous avez besoin d’un caractère littéral `%` dans la requête, vous devez le quadrupler (`%%%%`) dans l’attribut `template` car la chaîne est interpolée deux fois : une fois pendant l’interpolation du gabarit dans `as_sql()`, et une autre fois dans l’interpolation SQL avec les paramètres de requête dans le curseur de base de données.

  arg_joiner

    Un attribut de classe qui précise la séquence de caractères utilisée pour joindre les éléments de la liste d”`expressions`. Sa valeur par défaut est `', '`.

  arity

    Un attribut de classe qui indique le nombre de paramètres acceptés par la fonction. Si cet attribut est défini et que la fonction est appelée avec un nombre d’expressions différent, une exception `TypeError` sera générée. La valeur par défaut est `None`.

  as_sql(compiler, connection, function=None, template=None, arg_joiner=None, **extra_context)

    Génère le fragment SQL de la fonction de base de données. Renvoie un tuple `(sql, params)`, où `sql` est la chaîne SQL et `params` est la liste ou le tuple des paramètres de requête.

    Les méthodes `as_vendor()` devraient utiliser les paramètres `function`, `template`, `arg_joiner` et tout autre paramètre `**extra_context` pour adapter le code SQL en cas de besoin. Par exemple :

    `django/db/models/functions.py`

        class ConcatPair(Func):
            ...
            function = "CONCAT"
            ...

            def as_mysql(self, compiler, connection, **extra_context):
                return super().as_sql(
                    compiler,
                    connection,
                    function="CONCAT_WS",
                    template="%(function)s('', %(expressions)s)",
                    **extra_context
                )

    Pour éviter une vulnérabilité d’injection SQL, `extra_context` ne doit pas contenir de contenu non sûr provenant des utilisateurs car ces valeurs sont insérées dans la chaîne SQL et non passées comme paramètres de requête, auquel cas le pilote de base de données les aurait échappés.

Le paramètre `*expressions` est une liste d’expressions positionnelles auxquelles la fonction s’applique. Les expressions sont converties en chaînes, jointes par `arg_joiner`, puis insérées dans `template` par le substituant `expressions`.

Les paramètres positionnels peuvent être des expressions ou des valeurs Python. Les chaînes sont censées être des références de colonnes et seront enveloppées dans des expressions `F()` alors que les autres valeurs sont enveloppées dans des expressions `Value()`.

Les paramètres nommés `**extra` sont des paires `clé=valeur` qui peuvent être insérées dans l’attribut `template`. Pour éviter une vulnérabilité d’injection SQL, `extra` ne doit pas contenir de contenu non sûr provenant des utilisateurs car ces valeurs sont insérées dans la chaîne SQL et non passées comme paramètres de requête, auquel cas le pilote de base de données les aurait échappés.

Les mots-clés `function`, `template` et `arg_joiner` peuvent être utilisés pour remplacer les attributs de même nom sans avoir besoin de définir une classe spécifique. output_field peut être utilisé pour définir le type de la valeur renvoyée.

Sanitize input used to configure a query expression

Built-in database functions (such as `Cast`) vary in whether arguments such as `output_field` can be supplied as positional arguments or only by keyword. For `output_field` and several other cases, the input ultimately reaches `Func()` as a keyword argument, so the advice to avoid constructing keyword arguments from untrusted user input applies as equally to these arguments as it does to `**extra`.

### Expressions `Aggregate()`

Une expression d’agrégation est un cas particulier d’une expression Func() qui informe la requête qu’une clause `GROUP BY` est nécessaire. Toutes les fonctions d’agrégation comme `Sum()` et `Count()` héritent de `Aggregate()`.

Comme les `Aggregate` sont des des expressions et enveloppent des expressions, il est possible de représenter des calculs complexes :

    from django.db.models import Count

    Company.objects.annotate(
        managers_required=(Count("num_employees") / 4) + Count("num_managers")
    )

L’API `Aggregate` se présente comme suit :

class Aggregate(*expressions, output_field=None, distinct=False, filter=None, default=None, order_by=None, **extra)
  template

    Un attribut de classe, sous forme de chaîne de format, qui décrit le code SQL généré pour cette agrégation. Sa valeur par défaut est `'%(function)s(%(distinct)s%(expressions)s)'`.

  function

    Un attribut de classe décrivant la fonction d’agrégation qui sera générée. Plus précisément, `function` sera inséré par le substituant `function` à l’intérieur de `template`. Sa valeur par défaut est `None`.

  window_compatible

    Vaut `True` par défaut car la plupart des fonctions d’agrégat peuvent être utilisées comme expression source dans `Window`.

  allow_distinct

    Un attribut de classe déterminant si cette fonction d’agrégat permet de passer un paramètre nommé `distinct`. Si défini à `False` (par défaut), `TypeError` est générée si `distinct=True` est transmis.

  allow_order_by
    New in Django 6.0.

    A class attribute determining whether or not this aggregate function allows passing a `order_by` keyword argument. If set to `False` (default), `TypeError` is raised if `order_by` is passed as a value other than `None`.

  empty_result_set_value

    Vaut `None` par défaut car la plupart des fonctions d’agrégat renvoient `NULL` lorsqu’elles sont appliquées à un jeu de résultats nul.

Les paramètres positionnels `expressions` peuvent inclure des expressions, des transformations du champ de modèle ou des noms de champs de modèle. Ils seront convertis en texte et utilisé comme substituant `expressions` à l’intérieur de `template`.

Le paramètre `distinct` détermine si la fonction d’agrégat doit être invoquée pour chaque valeur distincte de `expressions` (ou ensemble de valeurs, en cas d’expressions multiples). Le paramètre n’est pris en charge que pour les agrégats qui possèdent `allow_distinct` à `True`.

The `filter` argument takes a Q object that’s used to filter the rows that are aggregated. See Agrégation conditionnelle and Filtrage sur les annotations for example usage.

The `order_by` argument behaves similarly to the `field_names` input of the `order_by()` function, accepting a field name (with an optional `"-"` prefix which indicates descending order) or an expression (or a tuple or list of strings and/or expressions) that specifies the ordering of the elements in the result.

L’argument `default` prend une valeur qui sera transmise avec l’agrégat à `Coalesce`. C’est utile pour indiquer une valeur à renvoyer autre que `None` lorsque le résultat de requête (ou groupement) ne contient aucune ligne.

Les paramètres nommés `**extra` sont des paires `clé=valeur` qui peuvent être insérées par interpolation dans l’attribut `template`.

Changed in Django 6.0:

The `order_by` argument was added.

### Création de ses propres fonctions d’agrégation

Vous pouvez aussi créer vos propres fonctions d’agrégation. Il faut définir au minimum `function`, mais vous pouvez aussi personnaliser complètement le code SQL produit. Voici un court exemple :

    from django.db.models import Aggregate

    class Sum(Aggregate):
        # Supports SUM(ALL field).
        function = "SUM"
        template = "%(function)s(%(all_values)s%(expressions)s)"
        allow_distinct = False
        arity = 1

        def __init__(self, expression, all_values=False, **extra):
            super().__init__(expression, all_values="ALL " if all_values else "", **extra)

### Expressions `Value()`

class Value(value, output_field=None)

Un objet `Value()` représente le composant le plus petit possible d’une expression : une simple valeur. Lorsque vous avez besoin de représenter la valeur d’un nombre entier, d’un booléen ou d’une chaîne à l’intérieur d’une expression, vous pouvez insérer cette valeur dans un objet `Value()`.

Il est rarement nécessaire de faire appel directement à `Value()`. Lorsque vous écrivez l’expression `F('champ') + 1`, Django insère implicitement le `1` dans un objet `Value()`, ce qui permet d’utiliser des valeurs simples dans des expressions plus complexes. `Value()` devra être utilisé lors du passage d’une chaîne à une expression. La plupart des expressions interprètent un paramètre textuel comme le nom d’un champ, comme par exemple `Lower('name')`.

Le paramètre `value` décrit la valeur à inclure dans l’expression, comme `1`, `True` ou `None`. Django sait comment convertir ces valeurs Python dans le bon type de la base de données correspondante.

Si aucun champ output_field n’est indiqué, il sera déduit du type de la valeur fournie pour un grand nombre de types courants. Par exemple, si on passe une instance de `datetime.datetime` comme valeur, la valeur par défaut de `output_field` sera `DateTimeField`.

### Expressions `ExpressionWrapper()`

class ExpressionWrapper(expression, output_field)

`ExpressionWrapper` enveloppe une autre expression et permet d’accéder aux propriétés, telles que output_field, qui pourraient ne pas être disponibles dans d’autres expressions. `ExpressionWrapper` est nécessaire lorsqu’on utilise de l’arithmétique avec les expressions `F()` avec différents types, comme décrit dans Utilisation de F() avec les annotations.

Database casting not performed

`ExpressionWrapper` only sets the output field for the ORM and does not perform any database-level casting. To ensure a specific type is returned from the database, use `Cast` instead.

### `JSONNull()` expression

New in Django 6.1.

class JSONNull

Specialized expression to represent JSON scalar `null` on a `JSONField`.

See Stockage et requête de None for usage examples.

### Expressions conditionnelles

Les expressions conditionnelles permettent d’utiliser de la logique `if` … `elif` … `else` dans les requêtes. Django prend nativement en charge les expressions SQL `CASE`. Pour plus de détails, consultez Expressions conditionnelles.

### Expressions `Subquery()`

class Subquery(queryset, output_field=None)

Il est possible d’ajouter des sous-requêtes explicites à un objet `QuerySet` en utilisant l’expression `Subquery`.

Par exemple, pour annoter chaque `post` avec l’adresse de courriel de l’auteur du commentaire le plus récent de ce `post`:

    >>> from django.db.models import OuterRef, Subquery
    >>> newest = Comment.objects.filter(post=OuterRef("pk")).order_by("-created_at")
    >>> Post.objects.annotate(newest_commenter_email=Subquery(newest.values("email")[:1]))

Avec PostgreSQL, le code SQL ressemble à ceci :

    SELECT "post"."id", (
        SELECT U0."email"
        FROM "comment" U0
        WHERE U0."post_id" = ("post"."id")
        ORDER BY U0."created_at" DESC LIMIT 1
    ) AS "newest_commenter_email" FROM "post"

Note

Les exemples de cette section sont conçus pour montrer comment forcer Django à exécuter une sous-requête. Dans certains cas, il serait possible d’écrire une requête équivalente qui effectue la même tâche plus clairement ou plus efficacement.

#### Référencement des colonnes de la requête parente

class OuterRef(field)

Utilisez `OuterRef` lorsqu’un jeu de requête dans une `Subquery` a besoin de se référer à un champ de la requête parente ou de sa transformation. Il se comporte comme une expression `F` sauf que le contrôle vérifiant s’il se réfère à un champ valable n’est pas effectué avant que la requête parente ne soit résolue.

Des instances de `OuterRef` peuvent être utilisées en lien avec des instances imbriquées de `Subquery` pour se référer à un jeu de requête supérieur qui n’est pas le parent immédiat. Par exemple, ce jeu de requête devrait se trouver dans une paire imbriquée d’instances `Subquery` pour être résolu correctement :

    >>> Book.objects.filter(author=OuterRef(OuterRef("pk")))

#### Limite d’une sous-requête à une seule colonne

Dans certaines circonstances, une seule colonne doit être renvoyée d’une sous-requête `Subquery`, par exemple quand on veut utiliser la sous-requête comme cible d’une recherche `__in`. Pour renvoyer tous les commentaires de `posts` publiés durant les dernières 24 heures :

    >>> from datetime import timedelta
    >>> from django.utils import timezone
    >>> one_day_ago = timezone.now() - timedelta(days=1)
    >>> posts = Post.objects.filter(published_at__gte=one_day_ago)
    >>> Comment.objects.filter(post__in=Subquery(posts.values("pk")))

Dans ce cas, la sous-requête doit utiliser `values()` pour ne renvoyer qu’une seule colonne : la clé primaire du `post`.

#### Limite de la sous-requête à une seule ligne

Pour empêcher une sous-requête de renvoyer plusieurs lignes, une segmentation (`[:1]`) du résultat de requête est appliquée :

    >>> subquery = Subquery(newest.values("email")[:1])
    >>> Post.objects.annotate(newest_commenter_email=subquery)

Dans ce cas, la sous-requête ne doit renvoyer qu’une seule colonne et une seule ligne : l’adresse électronique du commentaire créé le plus récent.

(L’utilisation de `get()` à la place d’une segmentation ne fonctionnerait pas car `OuterRef` ne peut pas être résolu tant que le résultat de requête n’est pas utilisé dans une sous-requête `Subquery`.)

#### Sous-requêtes `Exists()`

class Exists(queryset)

`Exists` est une sous-classe de `Subquery` utilisant une instruction SQL `EXISTS`. Dans bien des cas, la performance sera meilleure qu’une sous-requête car la base de données est capable de stopper l’évaluation de la sous-requête dès qu’une première ligne correspondante est trouvée.

Par exemple, pour annoter chaque `post` avec une indication de la présence ou non d’un commentaire durant la journée écoulée :

    >>> from django.db.models import Exists, OuterRef
    >>> from datetime import timedelta
    >>> from django.utils import timezone
    >>> one_day_ago = timezone.now() - timedelta(days=1)
    >>> recent_comments = Comment.objects.filter(
    ...     post=OuterRef("pk"),
    ...     created_at__gte=one_day_ago,
    ... )
    >>> Post.objects.annotate(recent_comment=Exists(recent_comments))

Avec PostgreSQL, le code SQL ressemble à ceci :

    SELECT "post"."id", "post"."published_at", EXISTS(
        SELECT (1) as "a"
        FROM "comment" U0
        WHERE (
            U0."created_at" >= YYYY-MM-DD HH:MM:SS AND
            U0."post_id" = "post"."id"
        )
        LIMIT 1
    ) AS "recent_comment" FROM "post"

Il n’est pas nécessaire de forcer `Exists` à se référer à une seule colonne, car les colonnes sont ignorées et un résultat booléen est renvoyé. De même, comme le tri ne joue pas de rôle dans une sous-requête SQL `EXISTS` et ne ferait que baisser la performance, il est automatiquement enlevé.

Il est possible d’effectuer un `NOT EXISTS` avec `~Exists()`.

#### Filtrage sur des expressions `Subquery()` ou `Exists()`

Les sous-requêtes `Subquery()` qui renvoient des valeurs booléennes et `Exists()` peuvent être utilisées comme `condition` dans les expressions `When`, ou directement pour filtrer un jeu de requête :

    >>> recent_comments = Comment.objects.filter(...)  # From above
    >>> Post.objects.filter(Exists(recent_comments))

Cela garantira que la sous-requête ne sera pas ajoutée aux colonnes `SELECT`, ce qui peut aboutir à de meilleures performances.

#### Utilisation d’agrégation dans une expression `Subquery`

Il est possible d’utiliser des agrégats dans une sous-requête, mais cela nécessite une combinaison spécifique de `filter()`, `values()` et `annotate()` afin d’obtenir un groupement correct dans la sous-requête.

En supposant que les deux modèles ont un champ `length`, voici comment trouver les `posts` dont la longueur est plus grande que la longueur totale de tous les commentaires ensemble :

    >>> from django.db.models import OuterRef, Subquery, Sum
    >>> comments = Comment.objects.filter(post=OuterRef("pk")).order_by().values("post")
    >>> total_comments = comments.annotate(total=Sum("length")).values("total")
    >>> Post.objects.filter(length__gt=Subquery(total_comments))

Le premier `filter(...)` limite la sous-requête aux paramètres qui nous intéressent. `order_by()` enlève le tri `ordering` par défaut (le cas échéant) du modèle `Comment`, `values('post')` agrège les commentaires par `Post`. Pour terminer, `annotate(...)` effectue l’agrégation. L’ordre dans lequel sont appliqués ces méthodes de requête est important. Dans ce cas, comme la sous-requête doit être limitée à une seule colonne, `values('total')` est obligatoire.

Il s’agit de la seule manière d’effectuer une agrégation à l’intérieur d’une sous-requête, car `aggregate()` procède à l’évaluation du jeu de requête (et lorsqu’il existe une `OuterRef`, celle-ci ne pourrait pas être résolue).

### Expressions SQL brutes

class RawSQL(sql, params, output_field=None)

Parfois, les expressions de base de données ne peuvent pas facilement exprimer une clause `WHERE` complexe. Dans ces cas limites, utilisez l’expression `RawSQL`. Par exemple :

    >>> from django.db.models.expressions import RawSQL
    >>> queryset.annotate(val=RawSQL("select col from sometable where othercol = %s", (param,)))

Ces expressions brutes ne sont pas toujours portables entre moteurs de base de données (parce que vous écrivez du code SQL explicitement) et elles violent le principe DRY (ne pas se répéter), il s’agit donc de les éviter autant que possible.

Les expressions `RawSQL` peuvent aussi être utilisées comme cible des filtres `__in`:

    >>> queryset.filter(id__in=RawSQL("select id from sometable where col = %s", (param,)))

Avertissement

Pour se protéger des `attaques par injection SQL<https://fr.wikipedia.org/wiki/Injection_SQL>`_, vous devez échapper tout paramètre pouvant être contrôlé par les utilisateurs en employant `params`. `params` est un paramètre obligatoire pour vous forcer à confirmer que vous n’effectuez pas d’interpolation de code SQL avec des données provenant des utilisateurs.

Vous devez aussi éviter de placer les substituants entre guillemets dans la chaîne SQL. Cet exemple est vulnérable à l’injection SQL en raison des guillemets autour de `%s`:

    RawSQL("select col from sometable where othercol = '%s'")  # unsafe!

Vous pouvez en apprendre davantage sur le fonctionnement de la protection contre les injections SQL de Django.

### Fonctions de fenêtrage

Les fonctions de fenêtrage fournissent une méthode pour appliquer des fonctions aux partitions. Au contraire d’une fonction d’agrégat normale qui calcule le résultat final pour chaque ensemble défini par la clause de groupement, les fonctions de fenêtrage opèrent sur des intervalles (frames) et des partitions, et calculent le résultat pour chaque ligne.

Vous pouvez définir plusieurs fenêtres dans la même requête, ce qui dans le contexte de l’ORM Django serait équivalent à inclure plusieurs expressions dans un appel à QuerySet.annotate(). L’ORM n’emploie pas les fenêtres nommées, elles font en fait partie des colonnes sélectionnées.

class Window(expression, partition_by=None, order_by=None, frame=None, output_field=None)
  template

    Vaut par défaut `%(expression)s OVER (%(window)s)`. Si seul le paramètre `expression` est donné, la clause de fenêtrage sera vide.

La classe `Window` est l’expression principale d’une clause `OVER`.

Le paramètre `expression` est soit une fonction de fenêtrage, une fonction d’agrégat ou une expression compatible avec une clause de fenêtrage.

Le paramètre `partition_by` accepte une expression ou une séquence d’expressions (les noms de colonnes doivent être enveloppés dans des objets `F`) qui contrôlent le partitionnement des lignes. Le partitionnement limite les lignes utilisées pour calculer le jeu de résultats.

Le champ output_field est défini soit comme paramètre, soit par l’expression.

Le paramètre `order_by` accepte une expression sur lesquelles vous pouvez appeler `asc()` et `desc()`, une chaîne de nom de champ (avec un préfixe `"-"` facultatif qui indique un tri descendant), ou un tuple ou une liste de chaînes ou d’expressions. Ce tri contrôle l’ordre dans lequel l’expression est appliquée. Par exemple, si vous faites la somme des lignes dans une partition, le premier résultat est la valeur de la première ligne, le deuxième est la somme des première et deuxième lignes.

The `frame` parameter specifies which other rows should be used in the computation. See Portées (frames) for details.

Par exemple, pour annoter chaque film avec la note moyenne des films du même studio dans le même genre et la même année :

    >>> from django.db.models import Avg, F, Window
    >>> Movie.objects.annotate(
    ...     avg_rating=Window(
    ...         expression=Avg("rating"),
    ...         partition_by=[F("studio"), F("genre")],
    ...         order_by="released__year",
    ...     ),
    ... )

Cela permet de comparer la note d’un film par rapport à ses semblables.

Il peut être intéressant d’appliquer plusieurs expressions sur la même fenêtre, c-à-d sur les mêmes partition et intervalle. Par exemple, l’exemple précédent pourrait être modifié pour inclure également la meilleure et la pire note dans chaque groupe de films (mêmes studio, genre et date de sortie) en utilisant trois fonctions de fenêtrage dans la même requête. La partition et le tri de l’exemple précédent sont extraits dans un dictionnaire pour réduire la répétition :

    >>> from django.db.models import Avg, F, Max, Min, Window
    >>> window = {
    ...     "partition_by": [F("studio"), F("genre")],
    ...     "order_by": "released__year",
    ... }
    >>> Movie.objects.annotate(
    ...     avg_rating=Window(
    ...         expression=Avg("rating"),
    ...         **window,
    ...     ),
    ...     best=Window(
    ...         expression=Max("rating"),
    ...         **window,
    ...     ),
    ...     worst=Window(
    ...         expression=Min("rating"),
    ...         **window,
    ...     ),
    ... )

Le filtrage sur les fonctions de fenêtrage est accepté tant que les expressions de requête ne sont pas disjonctives (elles n’utilisent pas `OR` ou `XOR` comme connecteur) et s’appliquent à un jeu de requête effectuant une agrégation.

Par exemple, une requête qui compte sur une agrégation et possède un filtre combiné par `OR` sur une fonction de fenêtrage et un champ n’est pas prise en charge. L’application de prédicats combinés post-agrégation pourrait provoquer l’inclusion de lignes qui seraient normalement exclues des groupes :

    >>> qs = Movie.objects.annotate(
    ...     category_rank=Window(Rank(), partition_by="category", order_by="-rating"),
    ...     scenes_count=Count("actors"),
    ... ).filter(Q(category_rank__lte=3) | Q(title__contains="Batman"))
    >>> list(qs)
    NotImplementedError: Heterogeneous disjunctive predicates against window functions
    are not implemented when performing conditional aggregation.

Parmi les moteurs de base de données inclus dans Django, MySQL, PostgreSQL et Oracle prennent en charge les expressions de fenêtrage. La prise en charge des diverses fonctionnalités des expressions de fenêtrage varie en fonction des bases de données. Par exemple, les options dans `asc()` et `desc()` ne sont pas toujours prises en charge. Consultez la documentation de votre base de données si nécessaire.

#### Portées (frames)

Pour une portée de fenêtre, vous pouvez choisir soit une suite de lignes basée sur des intervalles, soit une suite de lignes normale.

class ValueRange(start=None, end=None, exclusion=None)
  frame_type

    Cet attribut est défini à `'RANGE'`.

  PostgreSQL ne prend en charge `ValueRange` que de manière limitée et ne gère que les points de début et de fin standard, tels que `CURRENT ROW` et `UNBOUNDED FOLLOWING`.

class RowRange(start=None, end=None, exclusion=None)
  frame_type

    Cet attribut est défini à `'ROWS'`.

Les deux classes renvoient du SQL selon le gabarit :

    %(frame_type)s BETWEEN %(start)s AND %(end)s

class WindowFrameExclusion
  CURRENT_ROW
  GROUP
  TIES
  NO_OTHERS

L’argument `exclusion` permet d’exclure des lignes (`CURRENT_ROW`), des groupes (`GROUP`) et des « ties » (`TIES`) des cadres (frames) de fenêtres sur les bases de données prises en charge.

    %(frame_type)s BETWEEN %(start)s AND %(end)s EXCLUDE %(exclusion)s

Les portées réduisent le nombre de lignes utilisées pour calculer le résultat. Elles ciblent un point de départ donné jusqu’à un point d’arrivée. Les portées peuvent être utilisées avec ou sans partition, mais il est généralement utile d’indiquer un tri de la fenêtre pour s’assurer d’un résultat déterministe. Dans une portée, un pair est une ligne de valeur équivalente, ou toutes les lignes si aucune clause de tri n’est présente.

Le point de départ par défaut d’une portée est `UNBOUNDED PRECEDING` qui correspond à la première ligne de la partition. Le point d’arrivée est toujours explicitement inclus dans le code SQL généré par l’ORM et vaut par défaut `UNBOUNDED FOLLOWING`. La portée par défaut contient toutes les lignes de la partition jusqu’à la dernière ligne du jeu de données.

Les valeurs acceptées des paramètres `start` et `end` sont `None`, un nombre entier ou zéro. Un nombre négatif pour `start` produit `N PRECEDING`, alors que `None` produit `UNBOUNDED PRECEDING`. Dans le mode `ROWS`, un entier positif peut être utilisé pour `start` ce qui produira `N FOLLOWING`. Les entiers positifs sont acceptés pour `end` et produisent `N FOLLOWING`. Dans le mode `ROWS`, un entier négatif peut être utilisé pour `end` ce qui produira `N PRECEDING`. Pour `start` comme pour `end`, zéro renvoie `CURRENT ROW`.

Il y a une différence dans ce que `CURRENT ROW` contient. Lorsqu’il est défini en mode `ROWS`, l’intervalle commence et se termine avec la ligne en cours. Lorsqu’il est défini en mode `RANGE`, l’intervalle commence et se termine à la première ou à la dernière ligne du groupe en fonction de la clause de tri. Ainsi, `RANGE CURRENT ROW` évalue l’expression pour les lignes qui ont la même valeur définie par le tri. Comme le gabarit contient à la fois les points `start` et `end`, cela peut être exprimé par

    ValueRange(start=0, end=0)

Si les « semblables » d’un film sont définis comme les films publiés par le même studio dans le même genre et la même année, cet exemple `RowRange` annote chaque film avec la note moyenne des deux films précédents et des deux films suivants le film concerné :

    >>> from django.db.models import Avg, F, RowRange, Window
    >>> Movie.objects.annotate(
    ...     avg_rating=Window(
    ...         expression=Avg("rating"),
    ...         partition_by=[F("studio"), F("genre")],
    ...         order_by="released__year",
    ...         frame=RowRange(start=-2, end=2),
    ...     ),
    ... )

Si la base de données le gère, vous pouvez définir les points de départ et de fin sur la base des valeurs d’une expression dans la partition. Si le champ `released` du model `Movie` stocke le mois de publication de chaque film, cet exemple `ValueRange` annote chaque film avec la note moyenne des films semblables publiés entre douze mois avant et douze mois après chaque film :

    >>> from django.db.models import Avg, F, ValueRange, Window
    >>> Movie.objects.annotate(
    ...     avg_rating=Window(
    ...         expression=Avg("rating"),
    ...         partition_by=[F("studio"), F("genre")],
    ...         order_by="released__year",
    ...         frame=ValueRange(start=-12, end=12),
    ...     ),
    ... )

## Informations techniques

Vous trouverez ci-après des détails d’implémentation technique pouvant être utiles aux auteurs de bibliothèques. L’API technique et les exemples qui suivent aident à créer des expressions de requête génériques pouvant étendre les fonctionnalités intégrées que Django propose.

### API d’expression

Les expressions de requête implémentent l’API d’expressions de requête, mais exposent également un certain nombre de méthodes et d’attributs supplémentaires énumérés ci-dessous. Toutes les expressions de requête doivent hériter de `Expression()` ou d’une sous-classe appropriée.

Lorsqu’une expression de requête englobe une autre expression, elle est responsable d’appeler les méthodes appropriées de l’expression englobée.

class Expression
  allowed_default

    Indique à Django que cette expression peut être utilisée dans `Field.db_default`. Vaut `False` par défaut.

  constraint_validation_compatible

    Indique à Django que cette expression peut être utilisée lors d’une validation de contrainte. Les expressions ayant `constraint_validation_compatible` définie à `False` ne peuvent avoir qu’une seule expression source. La valeur par défaut est `True`.

  contains_aggregate

    Indique à Django que cette expression contient une agrégation et qu’une clause `GROUP BY` doit être ajoutée à la requête.

  contains_over_clause

    Indique à Django que cette expression contient une expression `Window`. C’est par exemple utilisé pour interdire les expressions de fonction de fenêtrage dans les requêtes qui modifient des données.

  filterable

    Indique à Django que cette expression peut être référencée dans `QuerySet.filter()`. Vaut `True` par défaut.

  window_compatible

    Indique à Django que cette expression peut être utilisée comme expression source dans `Window`. Vaut `False` par défaut.

  empty_result_set_value

    Indique à Django la valeur qui doit être renvoyée lorsque l’expression est utilisée pour appliquer une fonction sur un jeu de requête vide. La valeur par défaut est `NotImplemented`, ce qui force l’expression à être calculée au niveau de la base de données.

  set_returning

    Indique à Django que cette expression contient une fonction renvoyant un ensemble (« set-returning »), provoquant une évaluation de sous-requête. Elle est utilisée par exemple pour permettre à certains fonctions PostgreSQL renvoyant des ensembles (par ex. `JSONB_PATH_QUERY`, `UNNEST`, etc.) d’omettre les optimisations et d’être évaluées correctement lorsque des annotations produisent elles-mêmes des lignes. Vaut `False` par défaut.

  allows_composite_expressions

    Indique à Django que cette expression permet des expressions composites, par exemple pour prendre en charges les clés primaires composites. Vaut `False` par défaut.

  resolve_expression(query=None, allow_joins=True, reuse=None, summarize=False, for_save=False)

    Offre l’opportunité d’effectuer un éventuel pré-traitement ou validation de l’expression avant que celle-ci ne soit ajoutée à la requête. `resolve_expression()` doit aussi être appelée pour toute expression imbriquée. Une copie `copy()` de `self` devrait être renvoyée avec les éventuelles transformations nécessaires.

    `query` est l’implémentation de la requête en fonction du moteur de base de données.

    `allow_joins` est une valeur booléenne permettant ou interdisant l’utilisation de jointures dans la requête.

    `reuse` est un ensemble de jointures réutilisables dans des scénarios de jointures multiples.

    `summarize` est une valeur booléenne qui, quand elle vaut `True`, signale que la requête en cours de calcul est une requête d’agrégation terminale.

    `for_save` est une valeur booléenne qui, quand elle vaut `True`, signale que la requête en cours d’exécution effectue une création ou une mise à jour.

  get_source_expressions()

    Renvoie une liste triée d’expressions internes. Par exemple :

        >>> Sum(F("foo")).get_source_expressions()
        [F('foo')]

  set_source_expressions(expressions)

    Accepte une liste d’expressions et les stocke afin que `get_source_expressions()` puisse les restituer.

  relabeled_clone(change_map)

    Renvoie un clone (copie) de `self`, sachant que tout alias de colonne aura été renommé. Les alias de colonnes sont renommés lorsque des sous-requêtes sont créées. `relabeled_clone()` doit également être appelée pour toute expression imbriquée et attribuée au clone.

    `change_map` est un dictionnaire faisant correspondre les anciens alias aux nouveaux.

    Exemple :

        def relabeled_clone(self, change_map):
            clone = copy.copy(self)
            clone.expression = self.expression.relabeled_clone(change_map)
            return clone

  convert_value(value, expression, connection)

    Un point d’entrée permettant à l’expression de forcer `value` à un type plus approprié.

    `expression` est la même chose que `self`.

  get_group_by_cols()

    Responsible for returning the list of columns referenced by this expression. `get_group_by_cols()` should be called on any nested expressions. `F()` objects, in particular, hold a reference to a column.

  asc(nulls_first=None, nulls_last=None)

    Renvoie l’expression prête à être triée dans l’ordre croissant.

    `nulls_first` et `nulls_last` définissent comment les valeurs nulles sont triées. Voir Utilisation de F() pour trier les valeurs nulles pour un exemple d’utilisation.

  desc(nulls_first=None, nulls_last=None)

    Renvoie l’expression prête à être triée dans l’ordre décroissant.

    `nulls_first` et `nulls_last` définissent comment les valeurs nulles sont triées. Voir Utilisation de F() pour trier les valeurs nulles pour un exemple d’utilisation.

  reverse_ordering()

    Renvoie `self` avec toute modification nécessaire pour inverser l’ordre de tri à l’intérieur d’un appel `order_by`. Comme exemple, une expression implémentant `NULLS LAST` changerait sa valeur pour devenir `NULLS FIRST`. Les modifications sont uniquement requises pour les expressions qui implémentent l’ordre de tri telles que `OrderBy`. Cette méthode est appelée lorsque `reverse()` est appelée sur un jeu de requête (`QuerySet`).

### Écriture de ses propres expressions de requête

Vous pouvez écrire vos propres classes d’expression de requête qui utilisent et peuvent s’intégrer avec d’autres expressions de requête. Prenons un exemple en écrivant une implémentation de la fonction SQL `COALESCE`, sans utiliser les expressions Func() intégrées.

La fonction SQL `COALESCE` est définie comme acceptant une liste de colonnes ou de valeurs. Elle renvoie la première colonne ou valeur qui n’est pas `NULL`.

Commençons par définir le gabarit à utiliser pour la génération du SQL ainsi qu’une méthode `__init__()` pour définir quelques attributs :

    from django.db.models import Expression

    class Coalesce(Expression):
        template = "COALESCE( %(expressions)s )"

        def __init__(self, expressions, output_field):
            super().__init__(output_field=output_field)
            if len(expressions) < 2:
                raise ValueError("expressions must have at least 2 elements")
            for expression in expressions:
                if not hasattr(expression, "resolve_expression"):
                    raise TypeError("%r is not an Expression" % expression)
            self.expressions = expressions

Nous effectuons quelques validations de base sur les paramètres, y compris le minimum de 2 colonnes ou valeurs, et nous nous assurons qu’il s’agisse bien d’expressions. output_field est ici obligatoire afin que Django sache à quel type de champ de modèle attribuer l’éventuel résultat.

Nous implémentons maintenant le prétraitement et la validation. Comme nous n’avons aucune validation propre à ce stade, nous la déléguons aux expressions imbriquées :

    def resolve_expression(
        self, query=None, allow_joins=True, reuse=None, summarize=False, for_save=False
    ):
        c = self.copy()
        c.is_summary = summarize
        for pos, expression in enumerate(self.expressions):
            c.expressions[pos] = expression.resolve_expression(
                query, allow_joins, reuse, summarize, for_save
            )
        return c

Ensuite, nous écrivons la méthode responsable de la génération du code SQL :

    def as_sql(self, compiler, connection, template=None):
        sql_expressions, sql_params = [], []
        for expression in self.expressions:
            sql, params = compiler.compile(expression)
            sql_expressions.append(sql)
            sql_params.extend(params)
        template = template or self.template
        data = {"expressions": ",".join(sql_expressions)}
        return template % data, tuple(sql_params)

    def as_oracle(self, compiler, connection):
        """
        Example of vendor specific handling (Oracle in this case).
        Let's make the function name lowercase.
        """
        return self.as_sql(compiler, connection, template="coalesce( %(expressions)s )")

Les méthodes `as_sql()` savent gérer des paramètres nommés personnalisés, permettantaux méthodes `as_nomfournisseur()` de remplacer des données utilisées pour générer la chaîne SQL. Il est préférable d’utiliser des paramètres nommés pour `as_sql()` pour la personnalisation que de transformer `self` dans les méthodes `as_nomfournisseur()` car cela peut produire des erreurs lors du fonctionnement sur d’autres moteurs de base de données. Si votre classe s’appuie sur des attributs de classe pour définir des données, considérez la possibilité de permettre des surcharges dans votre méthode `as_sql()`.

Nous générons le code SQL pour chacune des `expressions` en utilisant la méthode `compiler.compile()` et en combinant les résultats avec des virgules. Puis le gabarit est complété avec nos données et le code SQL est renvoyé avec ses paramètres.

Nous avons également défini une implémentation personnalisée spécifique au moteur Oracle. La fonction `as_oracle()` sera appelée à la place de `as_sql()` quand le moteur Oracle est en fonction.

Finalement, nous implémentons les autres méthodes qui permettent à notre expression de requête de collaborer harmonieusement avec d’autres expressions de requête :

    def get_source_expressions(self):
        return self.expressions

    def set_source_expressions(self, expressions):
        self.expressions = expressions

Voyons comment cela fonctionne :

    >>> from django.db.models import F, Value, CharField
    >>> qs = Company.objects.annotate(
    ...     tagline=Coalesce(
    ...         [F("motto"), F("ticker_name"), F("description"), Value("No Tagline")],
    ...         output_field=CharField(),
    ...     )
    ... )
    >>> for c in qs:
    ...     print("%s: %s" % (c.name, c.tagline))
    ...
    Google: Do No Evil
    Apple: AAPL
    Yahoo: Internet Company
    Django Software Foundation: No Tagline

#### Empêchement des injections SQL

Comme les paramètres nommés d’une fonction `Func` pour `__init__()` (`**extra`) et `as_sql()` (`**extra_context`) sont interpolés pour former une chaîne SQL et non pas transmis comme paramètres de requête (où le pilote de base de données se serait chargé de les échapper), ils ne doivent pas contenir des données non sûres soumises par des utilisateurs.

Par exemple, si le contenu de `substring` provient d’un utilisateur, cette fonction est vulnérable à l’injection SQL

    from django.db.models import Func

    class Position(Func):
        function = "POSITION"
        template = "%(function)s('%(substring)s' in %(expressions)s)"

        def __init__(self, expression, substring):
            # substring=substring is an SQL injection vulnerability!
            super().__init__(expression, substring=substring)

Cette fonction produit une chaîne SQL sans paramètre. Comme `substring` est transmis à `super().__init__()` comme paramètre nommé, il est interpolé dans la chaîne SQL avant que la requête soit envoyée à la base de données.

Voici une réécriture corrigée

    class Position(Func):
        function = "POSITION"
        arg_joiner = " IN "

        def __init__(self, expression, substring):
            super().__init__(substring, expression)

Quand `substring` est transmis sous forme de paramètre positionnel, il est ensuite transmis comme paramètre à la requête de base de données.

### Ajout de la prise en charge de moteurs de base de données externes

Si vous utilisez un moteur de base de données qui utilise une syntaxe SQL différente pour certaines fonctions, vous pouvez ajouter sa prise en charge en greffant une nouvelle méthode dans la classe de la fonction.

Admettons que nous écrivons un moteur pour SQL Server de Microsoft qui utilise l’instruction SQL `LEN` au lieu de `LENGTH` pour la fonction `Length`. Nous allons greffer une nouvelle méthode appelée `as_sqlserver()` dans la classe `Length`:

    from django.db.models.functions import Length

    def sqlserver_length(self, compiler, connection):
        return self.as_sql(compiler, connection, function="LEN")

    Length.as_sqlserver = sqlserver_length

Il est aussi possible de personnaliser le code SQL en utilisant le paramètre `template` de `as_sql()`.

Nous utilisons `as_sqlserver()` parce que `django.db.connection.vendor` renvoie `sqlserver` comme moteur.

Les moteurs tiers peuvent inscrire leurs fonctions dans le fichier `__init__.py` de premier niveau du paquet de moteur ou dans un fichier (ou paquet) `expressions.py` de premier niveauqui est lui-même importé depuis `__init__.py`.

Pour les projets d’utilisateurs qui souhaitent adapter le moteur qu’ils utilisent, ce code devrait se trouver dans une méthode `AppConfig.ready()`.