---
title: "Expressions conditionnelles"
version: 2.2
locale: fr
source: https://docs.djangoproject.com/fr/2.2/ref/models/conditional-expressions/
canonical: https://djangodocs.dev/fr/2.2/ref/models/conditional-expressions/
---
# Expressions conditionnelles

Les expressions conditionnelles permettent d’utiliser de la logique [`if`](https://docs.python.org/3/reference/compound_stmts.html#if) … [`elif`](https://docs.python.org/3/reference/compound_stmts.html#elif) … [`else`](https://docs.python.org/3/reference/compound_stmts.html#else) à l’intérieur des filtres, des annotations, des agrégations et des mises à jour. Une expression conditionnelle évalue une série de conditions pour chaque ligne d’une table et renvoie l’expression résultante correspondante. Les expressions conditionnelles peuvent également être combinées et imbriquées comme toute autre [expression](/fr/2.2/ref/models/expressions/).

## Les classes d’expressions conditionnelles

Nous allons utiliser le modèle suivant dans les exemples qui suivront :

```
from django.db import models

class Client(models.Model):
    REGULAR = 'R'
    GOLD = 'G'
    PLATINUM = 'P'
    ACCOUNT_TYPE_CHOICES = [
        (REGULAR, 'Regular'),
        (GOLD, 'Gold'),
        (PLATINUM, 'Platinum'),
    ]
    name = models.CharField(max_length=50)
    registered_on = models.DateField()
    account_type = models.CharField(
        max_length=1,
        choices=ACCOUNT_TYPE_CHOICES,
        default=REGULAR,
    )
```

### `When`

#### `class When(condition=None, then=None, **lookups)`

Un objet `When()` est utilisé pour englober une condition et ses résultats pour leur exploitation dans une expression conditionnelle. L’emploi d’un objet `When()` est semblable à celui d’une méthode [`filter()`](/fr/2.2/ref/models/querysets/#django.db.models.query.QuerySet.filter). La condition peut être indiquée en utilisant des objets de [recherche de champ](/fr/2.2/ref/models/querysets/#field-lookups) ou des objets [`Q`](/fr/2.2/ref/models/querysets/#django.db.models.Q). Le résultat est fourni en utilisant le mot-clé `then`.

Quelques exemples :

```
>>> from django.db.models import F, Q, When
>>> # String arguments refer to fields; the following two examples are equivalent:
>>> When(account_type=Client.GOLD, then='name')
>>> When(account_type=Client.GOLD, then=F('name'))
>>> # You can use field lookups in the condition
>>> from datetime import date
>>> When(registered_on__gt=date(2014, 1, 1),
...      registered_on__lt=date(2015, 1, 1),
...      then='account_type')
>>> # Complex conditions can be created using Q objects
>>> When(Q(name__startswith="John") | Q(name__startswith="Paul"),
...      then='name')
```

N’oubliez pas que chacune de ces valeurs peut être elle-même une expression.

> **Note**
>
> Comme le paramètre nommé `then` est réservé au résultat de l’expression `When()`, un conflit potentiel existe si un [`Model`](/fr/2.2/ref/models/instances/#django.db.models.Model) possède un champ nommé `then`. Ceci peut être résolu de deux manières :
>
> ```
> >>> When(then__exact=0, then=1)
> >>> When(Q(then=0), then=1)
> ```

### `Case`

#### `class Case(*cases, **extra)`

Une expression `Case()` est semblable à une instruction [`if`](https://docs.python.org/3/reference/compound_stmts.html#if) … [`elif`](https://docs.python.org/3/reference/compound_stmts.html#elif) … [`else`](https://docs.python.org/3/reference/compound_stmts.html#else) en Python. Chaque `condition` dans les objets `When()` fournis est évaluée dans l’ordre, jusqu’à ce que l’une d’elle résulte en une valeur vraie. L’expression `result` de l’objet `When()` correspondant est renvoyée.

Un exemple simple :

```
>>>
>>> from datetime import date, timedelta
>>> from django.db.models import Case, CharField, Value, When
>>> Client.objects.create(
...     name='Jane Doe',
...     account_type=Client.REGULAR,
...     registered_on=date.today() - timedelta(days=36))
>>> Client.objects.create(
...     name='James Smith',
...     account_type=Client.GOLD,
...     registered_on=date.today() - timedelta(days=5))
>>> Client.objects.create(
...     name='Jack Black',
...     account_type=Client.PLATINUM,
...     registered_on=date.today() - timedelta(days=10 * 365))
>>> # Get the discount for each Client based on the account type
>>> Client.objects.annotate(
...     discount=Case(
...         When(account_type=Client.GOLD, then=Value('5%')),
...         When(account_type=Client.PLATINUM, then=Value('10%')),
...         default=Value('0%'),
...         output_field=CharField(),
...     ),
... ).values_list('name', 'discount')
<QuerySet [('Jane Doe', '0%'), ('James Smith', '5%'), ('Jack Black', '10%')]>
```

`Case()` accepte un nombre indéfini d’objets `When()` comme paramètres individuels. D’autres options sont fournies sous forme de paramètres nommés. Si aucune des conditions évaluées ne résulte en une valeur vraie, c’est alors l’expression indiquée dans le paramètre nommé `default` qui est renvoyée. Si aucun paramètre `default` n’est fourni, c’est `None` qui est utilisé.

Si nous voulions modifier notre requête précédente pour obtenir le rabais sur la base de la fidélité de `Client`, nous pourrions le faire à l’aide d’expressions de recherche :

```
>>> a_month_ago = date.today() - timedelta(days=30)
>>> a_year_ago = date.today() - timedelta(days=365)
>>> # Get the discount for each Client based on the registration date
>>> Client.objects.annotate(
...     discount=Case(
...         When(registered_on__lte=a_year_ago, then=Value('10%')),
...         When(registered_on__lte=a_month_ago, then=Value('5%')),
...         default=Value('0%'),
...         output_field=CharField(),
...     )
... ).values_list('name', 'discount')
<QuerySet [('Jane Doe', '5%'), ('James Smith', '0%'), ('Jack Black', '10%')]>
```

> **Note**
>
> Rappelez-vous que les conditions sont évaluées dans l’ordre, ce qui fait que dans l’exemple ci-dessus, nous obtenons le résultat correct même si la seconde condition correspondait à la fois à Jane Doe et à Jack Black. Cela fonctionne exactement de la même manière qu’avec l’instruction [`if`](https://docs.python.org/3/reference/compound_stmts.html#if) … [`elif`](https://docs.python.org/3/reference/compound_stmts.html#elif) … [`else`](https://docs.python.org/3/reference/compound_stmts.html#else) en Python.

`Case()` fonctionne aussi dans une clause `filter()`. Par exemple, pour trouver tous les clients « gold » qui se sont inscrits il y a plus d’un mois et les clients « platinum » qui se sont inscrits il y a plus d’un an :

```
>>> a_month_ago = date.today() - timedelta(days=30)
>>> a_year_ago = date.today() - timedelta(days=365)
>>> Client.objects.filter(
...     registered_on__lte=Case(
...         When(account_type=Client.GOLD, then=a_month_ago),
...         When(account_type=Client.PLATINUM, then=a_year_ago),
...     ),
... ).values_list('name', 'account_type')
<QuerySet [('Jack Black', 'P')]>
```

## Requêtes avancées

Les expressions conditionnelles peuvent être utilisées dans les annotations, les agrégations, les recherches et les mises à jour. Elles peuvent également être combinées et imbriquées avec d’autres expressions. Cela permet d’effectuer des requêtes conditionnelles puissantes.

### Mise à jour conditionnelle

Admettons que nous voulions modifier le type de compte `account_type` de nos clients pour qu’il corresponde aux dates d’enregistrement. Nous pouvons le faire à l’aide d’une expression conditionnelle et de la méthode [`update()`](/fr/2.2/ref/models/querysets/#django.db.models.query.QuerySet.update):

```
>>> a_month_ago = date.today() - timedelta(days=30)
>>> a_year_ago = date.today() - timedelta(days=365)
>>> # Update the account_type for each Client from the registration date
>>> Client.objects.update(
...     account_type=Case(
...         When(registered_on__lte=a_year_ago,
...              then=Value(Client.PLATINUM)),
...         When(registered_on__lte=a_month_ago,
...              then=Value(Client.GOLD)),
...         default=Value(Client.REGULAR)
...     ),
... )
>>> Client.objects.values_list('name', 'account_type')
<QuerySet [('Jane Doe', 'G'), ('James Smith', 'R'), ('Jack Black', 'P')]>
```

### Agrégation conditionnelle

Et si nous voulions savoir combien de clients existent pour chaque type de compte ? Nous pouvons utiliser le paramètre `filter` des [fonctions d’agrégat](/fr/2.2/ref/models/querysets/#aggregation-functions) pour pouvoir faire cela :

```
>>> # Create some more Clients first so we can have something to count
>>> Client.objects.create(
...     name='Jean Grey',
...     account_type=Client.REGULAR,
...     registered_on=date.today())
>>> Client.objects.create(
...     name='James Bond',
...     account_type=Client.PLATINUM,
...     registered_on=date.today())
>>> Client.objects.create(
...     name='Jane Porter',
...     account_type=Client.PLATINUM,
...     registered_on=date.today())
>>> # Get counts for each value of account_type
>>> from django.db.models import Count
>>> Client.objects.aggregate(
...     regular=Count('pk', filter=Q(account_type=Client.REGULAR)),
...     gold=Count('pk', filter=Q(account_type=Client.GOLD)),
...     platinum=Count('pk', filter=Q(account_type=Client.PLATINUM)),
... )
{'regular': 2, 'gold': 1, 'platinum': 3}
```

Cet agrégat produit une requête avec la syntaxe SQL 2003 `FILTER WHERE` pour les bases de données qui la prennent en charge :

```sql
SELECT count('id') FILTER (WHERE account_type=1) as regular,
       count('id') FILTER (WHERE account_type=2) as gold,
       count('id') FILTER (WHERE account_type=3) as platinum
FROM clients;
```

Pour les autres bases de données, cet effet est émulé à l’aide d’une instruction `CASE`:

```sql
SELECT count(CASE WHEN account_type=1 THEN id ELSE null) as regular,
       count(CASE WHEN account_type=2 THEN id ELSE null) as gold,
       count(CASE WHEN account_type=3 THEN id ELSE null) as platinum
FROM clients;
```

Les deux instructions SQL sont fonctionnellement équivalentes mais la variante `FILTER` plus explicite peut donner de meilleures performances.
