Reputation: 261
I had to use transaction.on_commit() for synchronous behaviour in one of the signals of my project. Though it works fine, I couldn't understand how does transaction.on_commit() decide which transaction to take. I mean there can be multiple transactions at the same time. But how does django know which transaction to take by using transaction.on_commit()
Upvotes: 1
Views: 1827
Reputation: 1847
Django uses thread-local storage to manage the transaction state per connection. And also, in Django, each database connection handles one transaction at a time. For more clarification, here it is the on_commit
function in transaction
module:
def on_commit(func, using=None):
"""
Register `func` to be called when the current transaction is committed.
If the current transaction is rolled back, `func` will not be called.
"""
get_connection(using).on_commit(func)
As you can see, there is a get_connection
function which looks like:
def get_connection(using=None):
"""
Get a database connection by name, or the default database connection
if no name is provided. This is a private API.
"""
if using is None:
using = DEFAULT_DB_ALIAS
return connections[using]
The connections
returns data form a private variable named self._connections
which is thread-local using asgiref.local.Local
.
So, there is only one unique connection per thread and when you call connections["default"]
or import the current connection from django.db import connection
, it returns the current thread-local database connection that has a single and unique transaction at a time.
This is why transaction.on_commit()
knows which transaction to hook on in Django.
Upvotes: 1
Reputation: 10392
If I understand the question correctly, I think the docs on Savepoints explains this.
Essentially, you can nest any number of transactions, but on_commit()
is only called after the top most one commits. However, on_commit()
that's nested within a savepoint will only be called if that savepoint was committed and all the ones above it are committed. So, it's tied to which ever one is currently open at the point it's called.
Upvotes: 1
Reputation: 449
According to the docs
You can also wrap your function in a lambda:
transaction.on_commit(lambda: some_celery_task.delay('arg1')) The function you pass in will be called immediately after a hypothetical database write made where on_commit() is called would be successfully committed.
If you call on_commit() while there isn’t an active transaction, the callback will be executed immediately.
If that hypothetical database write is instead rolled back (typically when an unhandled exception is raised in an atomic() block), your function will be discarded and never called.
If you are using it on post_save method with sender=SomeModel. Probably the on_commit is executed each time a SomeModel object is saved. Without the proper code we would not be able to tell the exact case.
Upvotes: 2