1. transaction.on_commit() c'est quoi ?

C'est une méthode d'enregistrement de callback utilisée dans Django pour exécuter une action juste après qu'une transaction de base de données ait été commitée avec succès.

from django.db import transaction

transaction.on_commit(my_function)

Lorsque ce code est exécuté, my_function() sera appelé après que la transaction actuelle soit terminée avec succès.

Image conceptuelle de django transaction.on_commit


2. Dans quelles situations l'utiliser ?

Situation Utilisation
Quand vous voulez exécuter quelque chose après avoir confirmé que les données sont bien stockées dans la DB ✅ Adapté
Quand vous voulez exécuter une tâche Celery après la transaction ✅ Adapté
Appels d'API externes, mise à jour des caches, etc. ✅ Adapté
Quand l'ordre des appels n'est pas important en dehors de la transaction ❌ Inadapté

3. Concepts clés

  • on_commit() n'a de sens que lorsque « une transaction est en cours d'exécution ».
  • S'il n'y a pas de transaction, le callback sera exécuté immédiatement!

4. Exemple incorrect

def my_view(request):
    def notify():
        print("Exécuté !")

    transaction.on_commit(notify)  # S'exécute immédiatement car il n'y a pas de transaction !

5. Exemple d'utilisation correct

from django.db import transaction

def my_view(request):
    with transaction.atomic():
        post = Post.objects.create(...)
        post.categories.add(...)

        def translate():
            translate_post.delay(post.id)

        transaction.on_commit(translate)

Dans cet exemple, translate() n'est exécuté qu'après que la création du post et l'ajout de catégories soient tous deux terminés.


6. Pourquoi est-ce important ?

Beaucoup de développeurs confondent les points suivants :

  • Les create() et add() de l'ORM ne signifient pas que les données sont effectivement commités
  • Avant le commit, tout est en état de « stockage logique »
  • Les tâches Celery s'exécutent dans un processus séparé, donc
  • Si elles s'exécutent avant le commit, elles référenceront des données incomplètes

7. Exemple idéal d'utilisation avec Celery

@shared_task
def post_finalize_task(post_id, categories, tags):
    post = Post.objects.get(id=post_id)

    with transaction.atomic():
        for name in categories:
            cat, _ = Category.objects.get_or_create(name=name)
            post.categories.add(cat)

        for name in tags:
            tag, _ = Tag.objects.get_or_create(name=name)
            post.tags.add(tag)

        def schedule_translation():
            translate_post.delay(post.id)

        transaction.on_commit(schedule_translation)

Cette structure garantit que la tâche translate_post ne sera exécutée qu'après que le stockage des catégories et des tags soit confirmé.


8. Résumé

Concept Description
Quand s'exécute-t-il ? Juste après qu'une transaction soit commitée avec succès
En cas d'échec ? Ne s'exécute jamais en cas de rollback
Pas de transaction ? S'exécute immédiatement (attention !)
Où l'utiliser ? À l'intérieur d'un bloc with transaction.atomic()
Exemples d'application Exécution de tâches Celery, envoi de notifications, mise à jour des caches, etc.

9. Commentaire de Jesse

transaction.on_commit() n'est pas une simple exécution différée.
C'est une expression claire de l'intention du développeur de n'exécuter le code que si la transaction est réussie,
et c'est un outil essentiel pour maintenir l'intégrité des données lorsque l'ORM et le traitement asynchrone sont utilisés ensemble.