# Documentation Django 6.0.7

# Transactions de base de données

Django donne quelques moyens de contrôler la gestion des transactions de base de données.

## Gestion des transactions de base de données

### Comportement de transaction par défaut de Django

Le comportement par défaut de Django est de fonctionner en mode de validation automatique (« autocommit »). Chaque requête est immédiatement validée dans la base de données, sauf si une transaction est en cours. Voir ci-dessous pour plus de détails.

Django utilise automatiquement des transactions ou des points de sauvegarde pour garantir l’intégrité des opérations de l’ORM qui nécessitent plusieurs requêtes, particulièrement les requêtes delete() et update().

La classe `TestCase` de Django englobe aussi chaque test dans une transaction pour des raisons de performance.

### Couplage des transactions aux requêtes HTTP

Une façon fréquente de gérer les transactions sur le Web est d’envelopper chaque requête dans une transaction. Définissez `ATOMIC_REQUESTS` à `True` dans la configuration de chaque base de données pour laquelle vous souhaitez activer ce comportement.

Voici comment cela fonctionne. Avant d’appeler une fonction de vue, Django démarre une transaction. Si la réponse est renvoyée sans problème particulier, Django valide la transaction. Si la vue génère une exception, Django annule la transaction.

Il est possible d’effectuer des sous-transactions à l’aide de points de sauvegarde dans le code de la vue, typiquement avec le gestionnaire de contexte `atomic()`. Cependant, quand la vue se termine, soit tous les changements sont validés, soit tous sont annulés.

Avertissement

Bien que la simplicité de ce modèle de transaction est attrayant, il devient inefficace lorsque le trafic augmente. L’ouverture d’une transaction pour chaque vue présente un certain coût. L’impact sur la performance dépend de la manière dont vos applications font usage des requêtes et de la manière dont la base de données gère les verrous.

Transactions par requête et réponses en flux

Lorsqu’une vue renvoie une réponse en flux (`StreamingHttpResponse`), la lecture du contenu de la réponse exécute fréquemment du code pour générer le contenu. Comme la vue s’est déjà terminée, ce code s’exécute en dehors de la transaction.

De manière générale, il n’est pas conseillé d’écrire dans la base de données durant la génération d’une réponse en flux, car il n’y a plus de méthode raisonnable pour gérer les erreurs après le début de l’envoi de la réponse.

En pratique, cette fonctionnalité enveloppe chaque fonction de vue dans le décorateur `atomic()` décrit ci-dessous.

Notez que seule l’exécution de la vue est incluse dans la transaction. Les intergiciels s’exécutent en dehors de la transaction, de même que le rendu des réponses par gabarit.

Lorsque `ATOMIC_REQUESTS` est actif, il est toujours possible d’empêcher les vues de s’exécuter dans une transaction.

non_atomic_requests(using=None)

  Ce décorateur annule l’effet de `ATOMIC_REQUESTS` pour une vue donnée :

      from django.db import transaction

      @transaction.non_atomic_requests
      def my_view(request):
          do_stuff()

      @transaction.non_atomic_requests(using="other")
      def my_other_view(request):
          do_stuff_on_the_other_database()

  Cela ne fonctionne que lorsque le décorateur est appliqué à la vue elle-même.

### Contrôle explicite des transactions

Django fournit une API unifiée pour contrôler les transactions de base de données.

atomic(using=None, savepoint=True, durable=False)

  L’atomicité est la propriété de base des transactions de base de données. `atomic` permet de créer un bloc de code à l’intérieur duquel l’atomicité est garantie au niveau de la base de données. Si le bloc de code se termine avec succès, les modifications sont validées dans la base de données. Si une exception apparaît, les modifications sont annulées en bloc.

  Les blocs `atomic` peuvent être imbriqués. Dans ce cas, lorsqu’un bloc intérieur se termine avec succès, ses effets peuvent encore être annulés si une exception est générée plus loin dans le bloc englobant.

  Il est parfois utile de s’assurer qu’un bloc `atomic` est toujours le bloc `atomic` le plus à l’extérieur, ce qui garantit que toute modification de base de données sera validée lorsque le bloc se termine sans erreur. On appelle cela la durabilité et on peut l’obtenir en définissant `durable=True`. Si ce bloc `atomic` est imbriqué à l’intérieur d’un autre, cela génère une erreur `RuntimeError`.

  `atomic` peut être utilisé comme décorateur:

      from django.db import transaction

      @transaction.atomic
      def viewfunc(request):
          # This code executes inside a transaction.
          do_stuff()

  et comme gestionnaire de contexte:

      from django.db import transaction

      def viewfunc(request):
          # This code executes in autocommit mode (Django's default).
          do_stuff()

          with transaction.atomic():
              # This code executes inside a transaction.
              do_more_stuff()

  L’insertion d”`atomic` dans un bloc try/except est une manière naturelle de gérer les erreurs d’intégrité :

      from django.db import IntegrityError, transaction

      @transaction.atomic
      def viewfunc(request):
          create_parent()

          try:
              with transaction.atomic():
                  generate_relationships()
          except IntegrityError:
              handle_exception()

          add_children()

  Dans cet exemple, même si `generate_relationships()` provoque une erreur de base de données en cassant une contrainte d’intégrité, vous pouvez exécuter des requêtes dans `add_children()` et les modifications de `create_parent()` sont toujours présentes et liées à la même transaction. Notez que toute opération exécutée dans `generate_relationships()` aura déjà été annulée proprement lorsque `handle_exception()` est appelée, ce qui fait que le gestionnaire d’exception peut très bien agir au niveau de la base de données si nécessaire.

  Évitez d’intercepter des exceptions à l’intérieur d”`atomic`!

  À la sortie d’un bloc `atomic`, Django examine si la sortie se fait normalement ou par une exception afin de déterminer s’il doit valider ou annuler la transaction. Si vous interceptez et gérez des exceptions à l’intérieur du bloc `atomic`, vous cachez à Django le fait qu’un problème est survenu. Cela peut aboutir à des comportements inattendus.

  Ce problème concerne plus spécifiquement les exceptions `DatabaseError` et ses sous-classes telles que `IntegrityError`. Après une telle erreur, la transaction est cassée et Django procédera à son annulation dès la sortie du bloc `atomic`. Si vous essayez d’exécuter des requêtes en base de données avant que l’annulation intervienne, Django générera une exception `TransactionManagementError`. Vous pouvez également rencontrer ce comportement lorsqu’un gestionnaire de signal lié à l’ORM génère une exception.

  La manière correcte d’intercepter les erreurs de base de données est de le faire autour du bloc `atomic` comme dans l’exemple ci-dessus. Si nécessaire, ajoutez un bloc `atomic` supplémentaire à cet effet. Cette stratégie présente un autre avantage : elle délimite explicitement les opérations qui seront annulées si une exception se produit.

  Si vous interceptez les exceptions générées par des requêtes SQL brutes, le comportement de Django n’est pas défini et dépend de la base de données.

  Vous devrez peut-être revenir manuellement à un précédent état de l’application en cas d’annulation d’une transaction.

  Les valeurs des champs d’un modèle ne seront pas réinitialisées lorsqu’une rétrogradation des transactions se produit. Cela pourrait conduire à un état de modèle incompatible à moins que vous ne restauriez manuellement les valeurs de champ d’origine.

  Par exemple, considérons `MyModel` avec un champ `active`, cet extrait garantit que la vérification à la fin `if obj.active` utilise la valeur correcte si la mise à jour de `active` vers `True` échoue dans la transaction

      from django.db import DatabaseError, transaction

      obj = MyModel(active=False)
      obj.active = True
      try:
          with transaction.atomic():
              obj.save()
      except DatabaseError:
          obj.active = False

      if obj.active:
          ...

  Ceci s’applique également à tout autre mécanisme pouvant conserver un état d’application, comme le cache ou les variables globales. Par exemple, si le code met à jour proactivement les données du cache après avoir enregistré un objet, il est recommandé d’utiliser plutôt transaction.on_commit() pour différer les modifications du cache jusqu’à ce que la transaction soit validée.

  Afin de garantir l’atomicité, `atomic` désactive certaines API. Si vous tentez de valider une transaction, de l’annuler ou de modifier l’état de validation automatique de la connexion de base de données à l’intérieur d’un bloc `atomic`, vous obtiendrez une exception.

  `atomic` accepte un paramètre `using` devant correspondre au nom d’une base de données. Si ce paramètre n’est pas présent, Django utilise la base de données `"default"`.

  En arrière-plan, le code de gestion des transactions de Django :

  - ouvre une transaction lorsqu’il entre dans le premier bloc `atomic`;

  - crée un point de sauvegarde lorsqu’il entre dans un bloc `atomic` imbriqué ;

  - libère le point de sauvegarde ou annule la transaction jusqu’au point de sauvegarde en quittant le bloc imbriqué ;

  - valide ou annule la transaction en quittant le bloc de départ.

  Vous pouvez désactiver la création de points de sauvegarde pour les blocs imbriqués en définissant le paramètre `savepoint` à `False`. Si une exception survient, Django procède à l’annulation de la transaction au moment de quitter le premier bloc ayant un point de sauvegarde, le cas échéant, ou le bloc initial sinon. L’atomicité est toujours garantie par la transaction du bloc initial. Cette option ne devrait être utilisée que si la création des points de sauvegarde affecte les performances de manière évidente. Le désavantage est que cela casse la gestion d’erreurs telle que décrite précédemment.

  Il est possible d’utiliser `atomic` lorsque la validation automatique est désactivée. Seuls des points de sauvegarde seront utilisés, même pour le bloc initial.

Considérations sur la performance

Les transactions ouvertes constituent une pénalité de performance pour votre serveur de base de données. Pour minimiser cet impact, gardez vos transactions aussi brèves que possible. C’est particulièrement important si vous utilisez `atomic()` dans des processus de longue durée, en dehors du cycle requête/réponse de Django.

## Autocommit

### Pourquoi Django utilise-t-il l’autocommit

Dans le standard SQL, chaque requête SQL démarre une transaction, sauf s’il y en a déjà une en cours. De telles transactions doivent ensuite être explicitement soit validées (commit), soit annulées (rollback).

Ce n’est pas toujours très pratique pour les développeurs d’applications. Pour contourner ce problème, la plupart des bases de données mettent à disposition un mode « autocommit ». Lorsque ce mode est actif et qu’il n’y a pas de transaction ouverte, chaque requête SQL est englobée dans sa propre transaction. En d’autres termes, chacune de ces requêtes non seulement démarre une transaction, mais cette transaction est aussi automatiquement validée ou annulée en fonction du résultat de la requête.

PEP 249, la spécification d’API de base de données Python v2.0, exige que le mode autocommit soit initialement désactivé. Django surcharge ce comportement par défaut et active le mode autocommit.

Pour empêcher cela, vous pouvez désactiver la gestion des transactions, mais ce n’est pas recommandé.

### Désactivation de la gestion des transaction

Vous pouvez désactiver totalement la gestion des transactions de Django pour une base de données précise en définissant `AUTOCOMMIT` à `False` dans sa configuration. Si vous faites cela, Django n’activera pas le mode autocommit et n’effectue aucune opération de commit. Vous obtenez alors le comportement habituel de la bibliothèque de base de données sous-jacente.

Cela demande que vous effectuiez un commit explicite de chaque transaction, même de celles initiées par Django ou par des bibliothèques tierces. Ainsi, cela convient mieux à des situations où vous souhaitez mettre en place votre propre intergiciel de gestion des transactions ou que vous faites des choses plutôt étranges.

## Lancement d’actions après le commit

Il peut arriver que vous ayez besoin d’effectuer une action liée à la transaction de base de données en cours, mais seulement si la transaction se termine par un commit réussi. On peut citer comme exemple une tâche d’arrière-plan, une notification par courriel ou une invalidation de cache.

`on_commit()` permet d’inscrire des fonctions de rappel qui seront exécutées après le commit réussi de la transaction ouverte :

on_commit(func, using=None, robust=False)

Passez une fonction, ou tout autre objet exécutable, à `on_commit()`:

    from django.db import transaction

    def send_welcome_email(): ...

    transaction.on_commit(send_welcome_email)

Les fonctions de rappel ne recevront aucun paramètre, mais vous pouvez en lier par la fonction `functools.partial()`:

    from functools import partial

    for user in users:
        transaction.on_commit(partial(send_invite_email, user=user))

Les fonctions de rappel sont appelées après que la transaction ouverte est validée avec succès. Si au contraire la transaction est annulée (rollback), typiquement lorsqu’une exception non traitée est générée dans le bloc `atomic()`, la fonction est ignorée et ne sera jamais appelée.

Si vous appelez `on_commit()` alors qu’aucune transaction n’est ouverte, la fonction transmise est immédiatement exécutée.

Il peut parfois être utile d’inscrire des fonctions de rappel qui peuvent échouer. En passant `robust=True`, cela permet à la fonction de rappel suivante d’être exécutée même quand la fonction en cours génère une exception. Toutes les erreurs dérivées de la classe `Exception` de Python sont interceptées et journalisées dans le journaliseur `django.db.backends.base`.

Vous pouvez utiliser `TestCase.captureOnCommitCallbacks()` pour tester les fonctions de rappel inscrites avec `on_commit()`.

### Points de sauvegarde (« savepoints »)

Les points de sauvegarde (blocs `atomic()` imbriqués) sont gérés correctement. C’est-à-dire qu’une fonction inscrite par `on_commit()` après un point de sauvegarde (dans un bloc `atomic()` imbriqué) sera appelée après le commit de la transaction la plus externe, mais pas dans le cas où une annulation (rollback) de ce point de sauvegarde ou de tout autre point de sauvegarde précédent se produit pendant la transaction :

    with transaction.atomic():  # Outer atomic, start a new transaction
        transaction.on_commit(foo)

        with transaction.atomic():  # Inner atomic block, create a savepoint
            transaction.on_commit(bar)

    # foo() and then bar() will be called when leaving the outermost block

D’un autre côté, lorsqu’un point de sauvegarde est annulé (en raison de l’apparition d’une exception), la fonction définie à l’intérieur de point de sauvegarde ne sera pas appelée :

    with transaction.atomic():  # Outer atomic, start a new transaction
        transaction.on_commit(foo)

        try:
            with transaction.atomic():  # Inner atomic block, create a savepoint
                transaction.on_commit(bar)
                raise SomeError()  # Raising an exception - abort the savepoint
        except SomeError:
            pass

    # foo() will be called, but not bar()

### Ordre d’exécution

Les fonctions `on_commit` d’une transaction donnée sont exécutées dans l’ordre de leur inscription.

### Gestion des exceptions

Si l’une des fonctions `on_commit` inscrites avec `robust=False` dans une transaction donnée génère une exception non traitée, aucune fonction inscrite à la suite dans cette même transaction ne sera exécutée. Ceci correspond au même comportement qui se serait produit si vous aviez exécuté ces fonctions séquentiellement vous-même sans `on_commit()`.

### Ordre d’exécution

Les fonctions de rappel sont exécutées après un commit réussi, ce qui fait qu’une exception dans une fonction de rappel ne provoquera pas d’annulation de transaction. Elles ne sont exécutées qu’en cas de succès de la transaction, mais sans faire partie de la transaction. Pour les cas d’utilisation visés (notifications par courriel, tâches d’arrière-plan, etc.) cela devrait convenir. Dans le cas contraire (par exemple si l’action à effectuer est si critique que son échec devrait aussi faire échouer la transaction principale), il ne faut alors pas exploiter le point d’entrée `on_commit()`. Une alternative possible est un commit en deux phases tel que la prise en charge du protocole de commit en deux phases de psycopg ou les extensions facultatives de commit en deux phases dans la spécification DB-API de Python.

Les fonctions de rappel ne sont pas appelées avant que la connexion ne repasse en mode autocommit après le commit (sinon toute requête dans une fonction de rappel ouvrirait une transaction implicite empêchant la connexion de repasser en mode de commit automatique).

Si l’on se trouve déjà en mode autocommit et en-dehors d’un bloc `atomic()`, la fonction est immédiatement exécutée sans attendre de commit.

Les fonctions `on_commit` ne fonctionnent qu’en mode autocommit et avec l’API de transaction `atomic()` (ou `ATOMIC_REQUESTS`). L’appel à `on_commit()` lorsque le mode autocommit est désactivé et en-dehors d’un bloc atomique produit une erreur.

### Utilisation dans les tests

La classe `TestCase` de Django enveloppe chaque test dans une transaction et annule cette transaction après chaque test, afin de garantir l’isolation des tests. Cela signifie qu’aucune transaction n’est réellement suivie d’un commit et donc qu’aucune des fonctions de rappel `on_commit()` ne sera jamais exécutée.

Vous pouvez surmonter cette restriction en utilisant `TestCase.captureOnCommitCallbacks()`. Cette méthode capture les fonctions de rappel `on_commit()` dans une liste, vous permettant de réaliser des assertions sur celles-ci ou d’émuler la validation de la transaction en les appelant.

Une autre manière de passer outre cette restriction est d’utiliser `TransactionTestCase` au lieu de `TestCase`. Cela signifie que les transactions sont validées et que les fonctions de rappel sont exécutées. Cependant, `TransactionTestCase` réinitialise la base de données entre les tests, ce qui est clairement plus lent que l’isolation apportée par `TestCase`.

### Pourquoi pas de point d’entrée pour les transactions annulées ?

Un point d’entrée pour les transactions annulées (rollback) est plus difficile à implémenter de manière solide qu’un point d’entrée de commit, car plusieurs choses peuvent provoquer une annulation implicite.

Par exemple, si la connexion à la base de données s’interrompt en raison d’un processus tué sans aucune chance de terminaison propre, le point d’entrée d’annulation ne sera jamais exécuté.

Mais il existe une solution : au lieu de faire quelque chose à l’intérieur du bloc atomique (transaction) puis de le défaire en cas d’échec de transaction, utilisez `on_commit()` pour déporter la tâche au moment où la transaction a réussi. C’est beaucoup plus facile de défaire quelque chose que l’on n’a jamais fait !

## API de bas niveau

Avertissement

Si possible, préférez toujours `atomic()`. Il prend en compte les particularités de chaque base de données et évite les opérations non valides.

L’API de bas niveau n’est utile que si vous implémentez votre propre gestion des transactions.

### Autocommit

Django fournit une API dans le module `django.db.transaction` pour gérer l’état de validation automatique de chaque connexion de base de données.

get_autocommit(using=None)

set_autocommit(autocommit, using=None)

Ces fonctions acceptent un paramètre `using` qui doit correspondre au nom d’une base de données. Si ce paramètre n’est pas présent, Django utilise la base de données `"default"`.

La validation automatique (« autocommit ») est initialement activée. Si vous la désactivez, il est de votre responsabilité de la restaurer ensuite.

Dès que vous désactivez la validation automatique, vous obtenez le comportement par défaut de votre adaptateur de base de données et Django ne vous aide plus. Même si ce comportement fait l’objet de la PEP 249, les implémentations des adaptateurs ne sont pas toujours cohérentes entre elles. Parcourez attentivement la documentation de l’adaptateur que vous utilisez.

Vous devez vous assurer qu’aucune transaction n’est pendante, généralement en appelant `commit()` ou `rollback()` avant de réactiver la validation automatique.

Django refuse de désactiver la validation automatique lorsqu’un bloc `atomic()` est actif, car l’atomicité ne serait alors plus respectée.

### Transactions

Une transaction est un ensemble atomique de requêtes de base de données. Même si votre programme se plante, la base de données garantit que soit tous les changements seront appliqués, soit aucun.

Django n’offre pas d’API pour démarrer une transaction. La manière attendue de démarrer une transaction est de désactiver la validation automatique avec `set_autocommit()`.

Une fois dans la transaction, vous pouvez choisir d’appliquer les modifications effectuées jusqu’à ce point avec `commit()`, ou de toutes les annuler avec `rollback()`. Ces fonctions sont définies dans `django.db.transaction`.

commit(using=None)

rollback(using=None)

Ces fonctions acceptent un paramètre `using` qui doit correspondre au nom d’une base de données. Si ce paramètre n’est pas présent, Django utilise la base de données `"default"`.

Django refuse de valider ou d’annuler une transaction lorsqu’un bloc `atomic()` est actif, car l’atomicité ne serait alors plus respectée.

### Points de sauvegarde (« savepoints »)

Un point de sauvegarde est un marqueur dans une transaction qui vous permet d’annuler une transaction en partie, plutôt que dans sa totalité. Les points de sauvegarde sont disponibles pour les moteurs SQLite, PostgreSQL, Oracle et MySQL (avec le moteur de stockage InnoDB). D’autres moteurs fournissent les fonctions des points de sauvegarde, mais ces fonctions sont vides, elles ne font rien du tout.

Les points de sauvegarde ne sont pas particulièrement utiles quand la validation automatique est active, ce qui est le comportement par défaut de Django. Cependant, dès que vous ouvrez une transaction avec `atomic()`, vous accumulez une série d’opérations de base de données en attente de validation ou d’annulation. Lorsque vous annulez avec un « rollback », toute la transaction est annulée Les points de sauvegarde permettent d’annuler des opérations de manière plus sélective, plutôt que d’annuler en bloc comme le fait `transaction.rollback()`.

Lorsque le décorateur `atomic()` est imbriqué, il crée un point de sauvegarde pour permettre une validation ou une annulation partielle. Vous êtes fortement encouragé à utiliser `atomic()` plutôt que les fonctions présentées ci-dessous, mais elles font tout de même partie de l’API publique, et il n’est pas prévu de les rendre obsolètes.

Chacune de ces fonctions accepte un paramètre `using` devant correspondre au nom de la base de données pour laquelle le comportement s’applique. Si aucun paramètre `using` n’est transmis, c’est la base de données `"default"` qui est utilisée.

Les points de sauvegarde sont contrôlés par trois fonctions dans `django.db.transaction`:

savepoint(using=None)

  Obsolète depuis la version 6.1: `savepoint` has been renamed to `savepoint_create`.

  Crée un nouveau point de sauvegarde. Un point est marqué dans la transaction, à un état qui est reconnu comme « bon ». Renvoie l’identifiant du point de sauvegarde (`sid`).

savepoint_create(using=None)
  New in Django 6.1.

  Crée un nouveau point de sauvegarde. Un point est marqué dans la transaction, à un état qui est reconnu comme « bon ». Renvoie l’identifiant du point de sauvegarde (`sid`).

savepoint_commit(sid, using=None)

  Libère le point de sauvegarde `sid`. Les modifications effectuées depuis la création du point de sauvegarde sont intégrées dans la transaction.

savepoint_rollback(sid, using=None)

  Annule la transaction en revenant au point de sauvegarde `sid`.

Ces fonctions ne font rien si les points de sauvegarde ne sont pas pris en charge ou si la base de données est en mode de validation automatique.

Une fonction utilitaire est également disponible :

clean_savepoints(using=None)

  Réinitialise le compteur utilisé pour générer les identifiants uniques des points de sauvegarde.

L’exemple suivant illustre l’utilisation des points de sauvegarde :

    from django.db import transaction

    # open a transaction
    @transaction.atomic
    def viewfunc(request):
        a.save()
        # transaction now contains a.save()

        sid = transaction.savepoint_create()

        b.save()
        # transaction now contains a.save() and b.save()

        if want_to_keep_b:
            transaction.savepoint_commit(sid)
            # open transaction still contains a.save() and b.save()
        else:
            transaction.savepoint_rollback(sid)
            # open transaction now contains only a.save()

Les points de sauvegarde peuvent être utilisés pour se rétablir après une erreur de base de données en effectuant une annulation partielle des opérations. Si vous faites cela à l’intérieur d’un bloc `atomic()`, tout le bloc sera quand même annulé, car Django ne sait pas que vous avez géré la situation à un plus bas niveau ! Pour empêcher cela, vous pouvez contrôler le comportement d’annulation avec les fonctions suivantes.

get_rollback(using=None)

set_rollback(rollback, using=None)

En définissant le drapeau `rollback` à `True`, vous forcez une annulation lorsque vous sortez du bloc `atomic` le plus proche. Cela peut être utile pour provoquer une annulation sans générer d’exception.

En le définissant à `False`, vous empêchez une telle annulation. Avant de faire cela, assurez-vous d’avoir bien annulé la transaction jusqu’à un point de sauvegarde en bon état à l’intérieur du bloc `atomic` actuel. Sinon, vous cassez l’atomicité et des corruptions de données peuvent apparaître.

## Notes spécifiques à certaines bases de données

### Points de sauvegarde dans

Même si les points de sauvegarde sont pris à charge par SQLite, un défaut de conception dans le module `sqlite3` les rend presque inutilisables.

Lorsque la validation automatique est active, les points de sauvegarde n’ont pas de raison d’être. Dans le cas contraire, `sqlite3` valide implicitement la transaction avant les instructions de points de sauvegarde (en fait, il valide avant toute instruction autre que `SELECT`, `INSERT`, `UPDATE`, `DELETE` et `REPLACE`). Ce bogue a deux conséquences :

- L’API de bas niveau des points de sauvegarde n’est utilisable qu’à l’intérieur d’une transaction, c’est-à-dire dans un bloc `atomic()`.

- Il est impossible d’utiliser `atomic()` lorsque la validation automatique est désactivée.

### Transactions dans MySQL

Si vous utilisez MySQL, la prise en charge des transactions par vos tables varie ; tout dépend de la version de MySQL et des types de tables que vous utilisez (par « type de table », nous entendons quelque chose comme « InnoDB » ou « MyISAM »). Les particularités des transactions de MySQL vont au-delà du thème de cette documentation, mais le site de MySQL possède des informations sur les transactions dans MySQL.

Si votre configuration MySQL ne gère pas les transactions, Django fonctionne toujours en mode de validation automatique : les instructions sont exécutées et validées dès qu’elles sont émises. Si votre configuration MySQL gère les transactions, Django traite les transactions comme expliqué dans ce document.

### Traitement des exceptions dans les transactions PostgreSQL

Note

Cette section n’a de sens que si vous implémentez votre propre gestion des transactions. Ce problème ne peut pas survenir dans le mode par défaut de Django et `atomic()` s’en charge automatiquement.

À l’intérieur d’une transaction, lorsque l’appel à un curseur PostgreSQL génère une exception (typiquement `IntegrityError`), toutes les commandes SQL suivantes dans la même transaction échouent avec l’erreur « current transaction is aborted, queries ignored until end of transaction block ». Bien qu’il soit improbable qu’une utilisation basique de `save()` génère une exception avec PostgreSQL, il y a des schémas d’utilisation plus pointus qui sont susceptibles de le faire, comme l’enregistrement d’objets avec des champs uniques, l’enregistrement avec les options `force_insert`/`force_update` ou l’appel à des instructions SQL personnalisées.

Il existe plusieurs manières de se sortir de ce genre d’erreurs.

#### Annulation de la transaction

La première option est d’annuler la totalité de la transaction. Par exemple :

    a.save()  # Succeeds, but may be undone by transaction rollback
    try:
        b.save()  # Could throw exception
    except IntegrityError:
        transaction.rollback()
    c.save()  # Succeeds, but a.save() may have been undone

L’appel de `transaction.rollback()` annule la totalité de la transaction. Toute opération de base de données non validée sera perdue. Dans cet exemple, les modifications effectuées par `a.save()` seront perdues, même si cette opération n’a pas elle-même généré d’erreur.

#### Annulation du point de sauvegarde

Vous pouvez utiliser des points de sauvegarde pour contrôler l’étendue d’une annulation. Avant d’effectuer une opération de base de données potentiellement délicate, vous pouvez définir ou mettre à jour le point de sauvegarde ; de cette façon, si l’opération échoue, vous pouvez annuler précisément l’opération concernée, plutôt que la totalité de la transaction. Par exemple :

    a.save()  # Succeeds, and never undone by savepoint rollback
    sid = transaction.savepoint_create()
    try:
        b.save()  # Could throw exception
        transaction.savepoint_commit(sid)
    except IntegrityError:
        transaction.savepoint_rollback(sid)
    c.save()  # Succeeds, and a.save() is never undone

Dans cet exemple, `a.save()` ne sera pas annulé dans le cas où `b.save()` génère une exception.