#2227: transaction.commit()/rollback() should be aware of nested calls to
transaction.managed(True)
-------------------------------------+-------------------------------------
Reporter: mderk@… | Owner: nobody
Type: New | Status: assigned
feature | Component: Database layer
Milestone: | (models, ORM)
Version: SVN | Severity: Normal
Resolution: | Keywords: transaction, nest,
Triage Stage: Accepted | scope, nested, rollback, commit
Needs documentation: 0 | Has patch: 1
Patch needs improvement: 1 | Needs tests: 0
UI/UX: 0 | Easy pickings: 0
-------------------------------------+-------------------------------------
Changes (by vzima):
* cc: vlastimil.zima@… (added)
* ui_ux: => 0
* easy: => 0
Comment:
I have stumbled into this problem when I tried to make post-save signal
code included in same transaction as Model.save(). Problem is that in
Model.save_base() a function commit_unless_managed() is called and
premature commit can only be avoided by making transaction managed.
Temporarily I solved this by adding check for transaction management into
save() method to prevent commit by commit_unless_managed() call
{{{
#!python
if not is_managed():
raise TransactionManagementError("This code must be under transaction
management")
}}}
But I found it better to solve this by appropriate decorator, such as this
{{{
#!python
def commit_on_success_unless_managed(using=None):
"""
This decorator activates commit on response unless code is under
transaction management.
"""
def inner_commit_on_success(func, db=None):
def _commit_on_success(*args, **kw):
# Only change from original commit_on_success is on next two
lines
if is_managed(using=db):
return func(*args, **kw)
try:
enter_transaction_management(using=db)
managed(True, using=db)
try:
res = func(*args, **kw)
except:
# All exceptions must be handled here (even string
ones).
if is_dirty(using=db):
rollback(using=db)
raise
else:
if is_dirty(using=db):
try:
commit(using=db)
except:
rollback(using=db)
raise
return res
finally:
leave_transaction_management(using=db)
return wraps(func)(_commit_on_success)
# Note that although the first argument is *called* `using`, it
# may actually be a function; @autocommit and @autocommit('foo')
# are both allowed forms.
if using is None:
using = DEFAULT_DB_ALIAS
if callable(using):
return inner_commit_on_success(using, DEFAULT_DB_ALIAS)
return lambda func: inner_commit_on_success(func, using)
}}}
This is not best solution, it is just idea how to solve things without
requirement for implementation of nested transactions.
--
Ticket URL: <https://code.djangoproject.com/ticket/2227#comment:29>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.
--
You received this message because you are subscribed to the Google Groups
"Django updates" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to
[email protected].
For more options, visit this group at
http://groups.google.com/group/django-updates?hl=en.