1. What is transaction.on_commit()?

It is a callback registration method used in Django when you want to execute some operation immediately after a database transaction has been successfully committed.

from django.db import transaction

transaction.on_commit(my_function)

When this code is executed, my_function() will run after the current transaction is successfully completed.

Django transaction.on_commit conceptual image


2. When to use it?

Situation Usage
When you want to execute something after confirming the data is saved to the DB ✅ Appropriate
When you want to execute a Celery task after the transaction ✅ Appropriate
For external API calls, cache refresh, etc. ✅ Appropriate
When the order of execution outside the transaction is not important ❌ Inappropriate

3. Key Concepts

  • on_commit() only makes sense when there is an active transaction.
  • If there is no transaction, the callback will be executed immediately!

4. Incorrect Example

def my_view(request):
    def notify():
        print("Executed!")

    transaction.on_commit(notify)  # Executes immediately as there is no transaction!

5. Correct Usage Example

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)

In the above example, translate() will only be executed after both the post creation and category addition are completed.


6. Why is it important?

Many developers confuse the following:

  • ORM's create() and add() does not mean that it is actually committed
  • Before the commit, everything is in a "logical save" state
  • Since a Celery task runs in a separate process,
  • If executed before the commit, it will reference incomplete data

7. Ideal Example of Using 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)

This structure ensures that the translate_post task is only executed after the categories/tags are confirmed to be stored.


8. Summary

Concept Description
When does it execute? Immediately after the transaction is successfully committed
What if it fails? Will never execute if rollback occurs
What if there is no transaction? Executes immediately (caution!)
Where to use it? Inside with transaction.atomic() block
Practical examples Execute Celery tasks, send notifications, update caches, etc.

9. Jesse's Comment

transaction.on_commit() is not just a simple delayed execution.
It is a clear expression of the developer's intent to execute only if the transaction succeeds,
and is an essential tool to ensure data integrity when using ORM and asynchronous processing together.