#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.

Reply via email to