---
title: "Gestion du code asynchrone"
version: 6.0
locale: fr
source: https://docs.djangoproject.com/fr/6.0/topics/async/
canonical: https://djangodocs.dev/fr/6.0/topics/async/
---
# Gestion du code asynchrone

Django has support for writing asynchronous (« async ») views, along with an
entirely async-enabled request stack if you are running under [ASGI](/fr/6.0/howto/deployment/asgi/). Async views will still work under WSGI, but
with a small per-request adaptation cost (see [Performance](#async-performance)), and
without the ability to have efficient long-running requests.

Many parts of Django provide asynchronous APIs, including [the ORM](/fr/6.0/topics/db/queries/#async-queries), the cache framework, authentication, sessions, and signals.
For other code, the [`sync_to_async()`](#asgiref.sync.sync_to_async) adapter is a low-cost bridge (see
[Performance](#async-performance)). A wide range of async-native Python libraries can
also be integrated.

## Vues asynchrones

Toute vue peut être déclarée asynchrone en faisant renvoyer une coroutine de sa partie exécutable ; ceci se fait en principe avec `async def`. Pour une vue basée sur une fonction, cela signifie déclarer la vue entière comme `async def`. Pour une vue basée sur une classe, cela signifie définir les gestionnaires de méthodes tels que  `get()` et `post()` comme `async def` (pas la méthode `__init__()` ni `as_view()`).

> **Note**
>
> Django utilise `asgiref.sync.iscoroutinefunction` pour tester si la vue est asynchrone ou pas. Si vous implémentez votre propre méthode pour renvoyer une coroutine, prenez soin d’utiliser `asgiref.sync.markcoroutinefunction` pour que cette fonction renvoie `True`.

Avec un serveur WSGI, les vues asynchrones tournent dans leur propre boucle événementielle unique. Cela veut dire que vous pouvez utiliser sans problème des fonctionnalités asynchrones, telles que des requêtes HTTP asynchrones et concurrentes, mais vous ne bénéficierez pas des avantages d’une pile asynchrone.

Les bénéfices principaux sont de pouvoir servir des centaines de connexions sans faire appel aux files d’exécutions (threads) Python. Cela vous permet d’utiliser les flux lents et les interrogations lentes (slow streaming, long-polling), et autres techniques semblables pour les réponses.

Si vous souhaitez exploiter ces possibilités, vous devrez déployer Django en utilisant plutôt un serveur [ASGI](/fr/6.0/howto/deployment/asgi/).

> **Note**
>
> A fully asynchronous request stack requires async middleware end-to-end.
> Where a piece of synchronous middleware sits between an ASGI server and an
> async view, Django adapts it by running it in its own thread; see
> [Performance](#async-performance) for the cost trade-off.
>
> Django’s bundled middleware supports both [sync and async](/fr/6.0/topics/http/middleware/#async-middleware). Third-party middleware may not. To see which
> middleware Django adapts, turn on debug logging for the `django.request`
> logger and look for log messages about *« Asynchronous handler adapted for
> middleware … »*.

Que ce soit en mode ASGI ou WSGI, vous pouvez toujours exploiter la prise en charge asynchrone de manière sûre pour exécuter du code de manière concurrente plutôt qu’en série. C’est particulièrement pratique lorsqu’on interagit avec des API externes ou des stockages de données.

Si vous souhaitez appeler une partie de Django qui est encore synchrone, il vous faut l’envelopper dans un appel à [`sync_to_async()`](#asgiref.sync.sync_to_async). Par exemple

```
from asgiref.sync import sync_to_async

results = await sync_to_async(sync_function, thread_sensitive=True)(pk=123)
```

Si par accident vous essayez d’appeler une partie de Django qui est purement synchrone à partir d’une vue asynchrone, vous déclencherez la [protection de sécurité asynchrone](#async-safety) de Django qui vise à protéger vos données d’une éventuelle corruption.

### Décorateurs

Les décorateurs suivants peuvent être utilisés avec des fonctions de vues à la fois synchrones et asynchrones :

- [`cache_control()`](/fr/6.0/topics/http/decorators/#django.views.decorators.cache.cache_control)
- [`never_cache()`](/fr/6.0/topics/http/decorators/#django.views.decorators.cache.never_cache)
- [`no_append_slash()`](/fr/6.0/topics/http/decorators/#django.views.decorators.common.no_append_slash)
- [`csp_override()`](/fr/6.0/ref/csp/#django.views.decorators.csp.csp_override)
- [`csp_report_only_override()`](/fr/6.0/ref/csp/#django.views.decorators.csp.csp_report_only_override)
- [`csrf_exempt()`](/fr/6.0/ref/csrf/#django.views.decorators.csrf.csrf_exempt)
- [`csrf_protect()`](/fr/6.0/ref/csrf/#django.views.decorators.csrf.csrf_protect)
- [`ensure_csrf_cookie()`](/fr/6.0/ref/csrf/#django.views.decorators.csrf.ensure_csrf_cookie)
- [`requires_csrf_token()`](/fr/6.0/ref/csrf/#django.views.decorators.csrf.requires_csrf_token)
- [`sensitive_variables()`](/fr/6.0/howto/error-reporting/#django.views.decorators.debug.sensitive_variables)
- [`sensitive_post_parameters()`](/fr/6.0/howto/error-reporting/#django.views.decorators.debug.sensitive_post_parameters)
- [`gzip_page()`](/fr/6.0/topics/http/decorators/#django.views.decorators.gzip.gzip_page)
- [`condition()`](/fr/6.0/topics/http/decorators/#django.views.decorators.http.condition)
- `conditional_page()`
- [`etag()`](/fr/6.0/topics/http/decorators/#django.views.decorators.http.etag)
- [`last_modified()`](/fr/6.0/topics/http/decorators/#django.views.decorators.http.last_modified)
- [`require_http_methods()`](/fr/6.0/topics/http/decorators/#django.views.decorators.http.require_http_methods)
- [`require_GET()`](/fr/6.0/topics/http/decorators/#django.views.decorators.http.require_GET)
- [`require_POST()`](/fr/6.0/topics/http/decorators/#django.views.decorators.http.require_POST)
- [`require_safe()`](/fr/6.0/topics/http/decorators/#django.views.decorators.http.require_safe)
- [`vary_on_cookie()`](/fr/6.0/topics/http/decorators/#django.views.decorators.vary.vary_on_cookie)
- [`vary_on_headers()`](/fr/6.0/topics/http/decorators/#django.views.decorators.vary.vary_on_headers)
- `xframe_options_deny()`
- `xframe_options_sameorigin()`
- `xframe_options_exempt()`

Par exemple :

```
from django.views.decorators.cache import never_cache

@never_cache
def my_sync_view(request): ...

@never_cache
async def my_async_view(request): ...
```

### Requêtes et ORM

With some exceptions, Django can run ORM queries asynchronously:

```
async for author in Author.objects.filter(name__startswith="A"):
    book = await author.books.afirst()
```

Des notes détaillées peuvent être consultées dans [Requêtes asynchrones](/fr/6.0/topics/db/queries/#async-queries), mais en résumé :

- Toutes les méthodes `QuerySet` générant une requête SQL possèdent une variante asynchrone dotée du préfixe `a`.
- `async for` est pris en charge pour tous les QuerySets (y compris les résultats de `values()` et `values_list()`).

Asynchronous model methods that use the database are also supported:

```
async def make_book(*args, **kwargs):
    book = Book(...)
    await book.asave(using="secondary")

async def make_book_with_tags(tags, *args, **kwargs):
    book = await Book.objects.acreate(...)
    await book.tags.aset(tags)
```

Les transactions ne fonctionnent pas encore en mode asynchrone. Si vous écrivez un bout de code nécessitant un comportement transactionnel, nous vous recommandons de l’écrire sous forme d’une seule fonction synchrone et de l’appeler avec [`sync_to_async()`](#asgiref.sync.sync_to_async).

[Persistent database connections](/fr/6.0/ref/databases/#persistent-database-connections), set
via the [`CONN_MAX_AGE`](/fr/6.0/ref/settings/#std-setting-CONN_MAX_AGE) setting, should also be disabled in async mode.
Instead, use your database backend’s built-in connection pooling if available,
or investigate a third-party connection pooling option if required. As in
synchronous Django, concurrent requests in a single process share that pool, so
size it to the target in-flight query concurrency.

### Performance

When running in a mode that does not match the view (e.g. an async view under
WSGI, or a traditional sync view under ASGI), Django must emulate the other
call style to allow your code to run. The per-call cost of this adaptation is
small: tens of microseconds in the in-request ASGI path, where the running
event loop is reused, and a few hundred microseconds in the cold-start path
used by management commands, background tasks, and scripts. Against typical
request times measured in milliseconds, this is rarely visible in itself, but
can become so under GIL contention as the number of active threads grows.

If you find yourself wrapping individual rows or operations in a tight loop,
restructure your code so the loop runs inside a single [`sync_to_async()`](#asgiref.sync.sync_to_async)
(or [`async_to_sync()`](#asgiref.sync.async_to_sync)) crossing. The per-call cost of the context switch is
then spread across the whole loop and effectively disappears.

The same per-call adaptation cost applies to middleware. Django will attempt to
minimize the number of context-switches between sync and async. If you have an
ASGI server, but all your middleware and views are synchronous, it will switch
just once, before it enters the middleware stack.

However, if you put synchronous middleware between an ASGI server and an
asynchronous view, it will have to switch into sync mode for the middleware and
then back to async mode for the view. Django will also hold the sync thread
open for middleware exception propagation. For request/response views that hit
the ORM and return, this is not usually a meaningful penalty. It matters most
when you are using ASGI for high in-process concurrency over non-ORM I/O (for
example upstream HTTP fan-out, server-sent events, or other long-lived
requests), where the extra thread per request caps that concurrency.

Nous suggérons de faire vos propres tests de performance pour observer les différences entre ASGI et WSGI avec votre code. Dans certains cas, les performances peuvent être meilleures avec ASGI même pour une base de code purement synchrone car le code de traitement des requêtes fonctionne toujours de manière asynchrone. Mais en général, l’activation du mode ASGI n’est profitable que si votre code contient du code asynchrone.

### Gestion des déconnexions

Pour les requêtes de longue durée, un client peut se déconnecter avant que la vue renvoie une réponse. Dans ce cas, `asyncio.CancelledError` sera générée dans la vue. Vous pouvez intercepter cette erreur et la traiter si vous avez besoin d’effectuer un quelconque nettoyage :

```
async def my_view(request):
    try:
        # Do some work
        ...
    except asyncio.CancelledError:
        # Handle disconnect
        raise
```

Vous pouvez aussi [traiter les déconnexions de client dans les réponses en flux](/fr/6.0/ref/request-response/#request-response-streaming-disconnect).

## Isolation de code asynchrone

#### `DJANGO_ALLOW_ASYNC_UNSAFE`

Certain key parts of Django are not able to operate safely in an async
environment, as they have global state that is not coroutine-aware. These parts
of Django are classified as « async-unsafe », and are protected from execution in
an async environment. The synchronous API of the ORM is the main example, but
there are other parts that are also protected in this way.

Si vous essayez d’exécuter l’une de ces parties depuis un fil d’exécution où une *boucle événementielle s’exécute*, vous obtiendrez une erreur [`SynchronousOnlyOperation`](/fr/6.0/ref/exceptions/#django.core.exceptions.SynchronousOnlyOperation). Notez que vous n’avez pas besoin d’être directement dans une fonction asynchrone pour déclencher cette erreur. Si vous avez appelé une fonction synchrone directement depuis une fonction asynchrone sans utiliser [`sync_to_async()`](#asgiref.sync.sync_to_async) ou un équivalent, cela peut alors aussi arriver, car votre code se trouve encore dans un fil d’exécution avec une boucle événementielle active, même s’il n’est pas déclaré comme code asynchrone.

Si vous obtenez cette erreur, vous devriez corriger votre code pour qu’il n’appelle pas le code de manière fautive depuis un contexte asynchrone. Au lieu de cela, écrivez le code communiquant avec des fonctions non adaptées à l’asynchrone dans sa propre fonction synchrone et en l’appelant par [`asgiref.sync.sync_to_async()`](#asgiref.sync.sync_to_async) (ou toute autre méthode d’exécution de code synchrone dans son propre fil d’exécution).

Le contexte asynchrone peut vous être imposé par l’environnement dans lequel s’exécute le code Django. Par exemple, les carnets [Jupyter](https://jupyter.org/) et les shells interactifs [IPython](https://ipython.org) fournissent tous deux de manière transparente une boucle évènementielle active pour qu’il soit plus facile d’interagir avec des API asynchrones.

Si vous utilisez un shell IPython, vous pouvez désactiver cette boucle évènementielle en exécutant :

```shell
%autoawait off
```

comme commande à l’invite IPython. Cela permet d’exécuter du code synchrone sans que des erreurs [`SynchronousOnlyOperation`](/fr/6.0/ref/exceptions/#django.core.exceptions.SynchronousOnlyOperation) se produisent ; cependant, vous ne serez pas non plus capable d’appeler des API asynchrones par `await`. Pour réactiver la boucle évènementielle, exécutez :

```shell
%autoawait on
```

Si vous vous trouvez dans un environnement autre que IPython (ou que vous ne pouvez pas désactiver `autoawait` dans IPython pour une raison quelconque), que vous êtes *certain* qu’il n’y a aucun risque que le code soit lancé de manière concurrente et que vous avez *absolument* besoin de lancer ce code synchrone à partir d’un contexte asynchrone, vous pouvez alors désactiver l’avertissement en définissant la variable d’environnement [`DJANGO_ALLOW_ASYNC_UNSAFE`](#envvar-DJANGO_ALLOW_ASYNC_UNSAFE) à une valeur quelconque.

> **Warning**
>
> Si vous activez cette option et qu’un accès concurrent se produit sur des parties de Django non adaptées à l’asynchrone, vous pourriez expérimenter des pertes ou des corruptions de données. Soyez très prudent et n’utilisez pas cela dans des environnements de production.

Si vous devez faire cela depuis du code Python, faites-le avec `os.environ`:

```
import os

os.environ["DJANGO_ALLOW_ASYNC_UNSAFE"] = "true"
```

## Fonctions d’adaptation asynchrone

Il est nécessaire d’adapter le style d’appel lors des appels de code synchrone depuis un contexte asynchrone ou vice versa. Il existe pour cela deux fonctions d’adaptation dans le module `asgiref.sync`: [`async_to_sync()`](#asgiref.sync.async_to_sync) et [`sync_to_async()`](#asgiref.sync.sync_to_async). Elles sont utiles pour faire le passage entre les styles d’appel tout en préservant la compatibilité.

Ces fonctions d’adaptation sont largement utilisées dans Django. Le paquet [asgiref](https://pypi.org/project/asgiref/) lui-même fait partie du projet Django et il est automatiquement installé comme dépendance lorsqu’on installe Django avec `pip`.

### `async_to_sync()`

#### `async_to_sync(async_function, force_new_loop=False)`

Accepte une fonction asynchrone et renvoie une fonction synchrone qui l’enveloppe. Peut être utilisée sous forme directe ou comme décorateur

```
from asgiref.sync import async_to_sync

async def get_data(): ...

sync_get_data = async_to_sync(get_data)

@async_to_sync
async def get_other_data(): ...
```

La fonction asynchrone est exécutée dans la boucle événementielle du fil d’exécution actuel, le cas échéant. S’il n’y a pas de boucle événementielle, une nouvelle boucle est générée spécifiquement pour cette invocation asynchrone unique et arrêtée dès que la fonction est terminée. Quelle que soit la situation, la fonction asynchrone s’exécutera dans un fil d’exécution différent de celui du code appelant.

Les valeurs « threadlocals » et « contextvars » sont préservées de part et d’autres des exécutions.

[`async_to_sync()`](#asgiref.sync.async_to_sync) is essentially a more powerful version of the
[`asyncio.run()`](https://docs.python.org/3/library/asyncio-runner.html#asyncio.run) function in Python’s standard library. As well as ensuring
threadlocals work, it also enables the `thread_sensitive` mode of
[`sync_to_async()`](#asgiref.sync.sync_to_async) when that wrapper is used below it. In the cold path
(no running event loop) it pays the cost of starting a fresh event loop, like
[`asyncio.run()`](https://docs.python.org/3/library/asyncio-runner.html#asyncio.run); when an event loop is already running (the in-request ASGI
case), the running loop is reused and the cost drops accordingly.

### `sync_to_async()`

#### `sync_to_async(sync_function, thread_sensitive=True)`

Accepte une fonction synchrone et renvoie une fonction asynchrone qui l’enveloppe. Peut être utilisée sous forme directe ou comme décorateur

```
from asgiref.sync import sync_to_async

async_function = sync_to_async(sync_function, thread_sensitive=False)
async_function = sync_to_async(sensitive_sync_function, thread_sensitive=True)

@sync_to_async
def sync_function(): ...
```

Les valeurs « threadlocals » et « contextvars » sont préservées de part et d’autres des exécutions.

Les fonctions synchrones ont tendances à être écrites en partant du principe qu’elles s’exécutent toutes dans le fil d’exécution principal, ce qui fait que [`sync_to_async()`](#asgiref.sync.sync_to_async) dispose de deux modes d’exécution :

- `thread_sensitive=True` (valeur par défaut) : la fonction synchrone sera exécutée dans le même fil d’exécution que toutes les autres fonctions `thread_sensitive`. Ce fil sera le fil principal si celui-ci est synchrone et que vous utilisez la fonction enveloppeuse [`async_to_sync()`](#asgiref.sync.async_to_sync).
- `thread_sensitive=False`: la fonction synchrone sera exécutée dans un tout nouveau fil d’exécution qui sera ensuite détruit lorsque l’invocation sera terminée.

Le mode « thread-sensitive » est très spécial et fait beaucoup d’efforts pour exécuter toutes les fonctions dans le même fil d’exécution. Notez toutefois qu’il *compte sur l’utilisation de* [`async_to_sync()`](#asgiref.sync.async_to_sync) *au-dessus de lui dans la pile d’appels* pour lancer les choses correctement dans le fil d’exécution principal. Si vous utilisez `asyncio.run()` ou une méthode semblable, il se limite à exécuter les fonctions dépendantes du fil d’exécution dans un seul fil partagé, mais il ne s’agira pas du fil d’exécution principal.

La raison de sa présence dans Django est que de nombreuses bibliothèques, particulièrement les adaptateurs de base de données, exigent que leur accès se fasse dans le même fil d’exécution dans lequel elles ont été créées. De même, une grande quantité de code existant dans Django présuppose qu’il s’exécute entièrement dans le même fil d’exécution, par ex. un intergiciel ajoutant des éléments à une requête pour utilisation ultérieure dans les vues.

Plutôt que d’introduire des problèmes potentiels de compatibilité avec ce code, nous avons choisi d’ajouter ce mode afin que tout code synchrone existant dans Django soit exécuté dans le même fil d’exécution et reste donc pleinement compatible avec le mode asynchrone. Notez que le code synchrone sera toujours exécuté dans un fil d’exécution *différent* du code asynchrone appelant, ce qui implique que vous devez éviter de passer des pointeurs bruts de base de données ou d’autres références sensibles au fil d’exécution.

Within a single request, multiple `thread_sensitive` calls serialize on that
request’s worker thread, but each request gets its own per-context worker, so
concurrent requests do *not* serialize against each other. This mirrors
Django’s connection-per-thread model, and the same constraint applies in other
async database libraries, where concurrent queries on a single connection
serialize on a lock. To support more concurrent requests, increase the
connection pool size accordingly rather than disabling `thread_sensitive`.

En pratique, cette restriction signifie que vous ne devriez pas transmettre des fonctionnalités de l’objet de base de données `connection` lors de l’appel à `sync_to_async()`. Si vous le faites, cela déclenchera les contrôles de sécurité des fils d’exécution :

```pycon
# DJANGO_SETTINGS_MODULE=settings.py python -m asyncio
>>> import asyncio
>>> from asgiref.sync import sync_to_async
>>> from django.db import connection
>>> # In an async context so you cannot use the database directly:
>>> connection.cursor()
django.core.exceptions.SynchronousOnlyOperation: You cannot call this from
an async context - use a thread or sync_to_async.
>>> # Nor can you pass resolved connection attributes across threads:
>>> await sync_to_async(connection.cursor)()
django.db.utils.DatabaseError: DatabaseWrapper objects created in a thread
can only be used in that same thread. The object with alias 'default' was
created in thread id 4371465600 and this is thread id 6131478528.
```

Au lieu de cela, vous devriez encapsuler tous les accès à la base de données dans une fonction utilitaire pouvant être appelée par `sync_to_async()` sans vous baser sur l’objet de connexion dans le code appelant.
