Ticket #10771: django-transactions.diff

File django-transactions.diff, 8.3 KB (added by Alex Gaynor, 9 years ago)
  • django/db/transaction.py

    diff --git a/django/db/transaction.py b/django/db/transaction.py
    index 3c767f1..562fc49 100644
    a b called, a commit is made. 
    1111Managed transactions don't do those commits, but will need some kind of manual
    1212or implicit commits or rollbacks.
    1313"""
     14import sys
    1415
    1516try:
    1617    import thread
    try: 
    2021    from functools import wraps
    2122except ImportError:
    2223    from django.utils.functional import wraps  # Python 2.4 fallback.
    23 from django.db import connections, DEFAULT_DB_ALIAS
     24
    2425from django.conf import settings
     26from django.db import connections, DEFAULT_DB_ALIAS
    2527
    2628class TransactionManagementError(Exception):
    2729    """
    def savepoint_commit(sid, using=None): 
    257259# DECORATORS #
    258260##############
    259261
    260 def autocommit(using=None):
    261     """
    262     Decorator that activates commit on save. This is Django's default behavior;
    263     this decorator is useful if you globally activated transaction management in
    264     your settings file and want the default behavior in some view functions.
    265     """
    266     def inner_autocommit(func, db=None):
    267         def _autocommit(*args, **kw):
     262class Transaction(object):
     263    def __init__(self, entering, exiting, using):
     264        self.entering = entering
     265        self.exiting = exiting
     266        self.using = using
     267   
     268    def __enter__(self):
     269        self.entering(self.using)
     270   
     271    def __exit__(self, exc_type, exc_value, traceback):
     272        self.exiting(exc_value, self.using)
     273   
     274    def __call__(self, func):
     275        @wraps(func)
     276        def inner(*args, **kwargs):
     277            self.__enter__()
    268278            try:
    269                 enter_transaction_management(managed=False, using=db)
    270                 managed(False, using=db)
    271                 return func(*args, **kw)
    272             finally:
    273                 leave_transaction_management(using=db)
    274         return wraps(func)(_autocommit)
     279                res = func(*args, **kwargs)
     280            except:
     281                self.__exit__(*sys.exc_info())
     282                raise
     283            else:
     284                self.__exit__(None, None, None)
     285                return res
     286        return inner
    275287
     288def _transaction_func(entering, exiting, using):
    276289    # Note that although the first argument is *called* `using`, it
    277290    # may actually be a function; @autocommit and @autocommit('foo')
    278291    # are both allowed forms.
    279292    if using is None:
    280293        using = DEFAULT_DB_ALIAS
    281294    if callable(using):
    282         return inner_autocommit(using, DEFAULT_DB_ALIAS)
    283     return lambda func: inner_autocommit(func, using)
     295        return Transaction(entering, exiting, DEFAULT_DB_ALIAS)(using)
     296    return Transaction(entering, exiting, using)
    284297
    285298
     299def autocommit(using=None):
     300    """
     301    Decorator that activates commit on save. This is Django's default behavior;
     302    this decorator is useful if you globally activated transaction management in
     303    your settings file and want the default behavior in some view functions.
     304    """
     305    def entering(using):
     306        enter_transaction_management(managed=False, using=using)
     307        managed(False, using=using)
     308   
     309    def exiting(exc_value, using):
     310        leave_transaction_management(using=using)
     311   
     312    return _transaction_func(entering, exiting, using)
     313
    286314def commit_on_success(using=None):
    287315    """
    288316    This decorator activates commit on response. This way, if the view function
    def commit_on_success(using=None): 
    290318    a rollback is made. This is one of the most common ways to do transaction
    291319    control in web apps.
    292320    """
    293     def inner_commit_on_success(func, db=None):
    294         def _commit_on_success(*args, **kw):
    295             try:
    296                 enter_transaction_management(using=db)
    297                 managed(True, using=db)
     321    def entering(using):
     322        enter_transaction_management(using=using)
     323        managed(True, using=using)
     324   
     325    def exiting(exc_value, using):
     326        if exc_value is not None:
     327            if is_dirty(using=using):
     328                rollback(using=using)
     329        else:
     330            if is_dirty(using=using):
    298331                try:
    299                     res = func(*args, **kw)
     332                    commit(using=using)
    300333                except:
    301                     # All exceptions must be handled here (even string ones).
    302                     if is_dirty(using=db):
    303                         rollback(using=db)
     334                    rollback(using=using)
    304335                    raise
    305                 else:
    306                     if is_dirty(using=db):
    307                         try:
    308                             commit(using=db)
    309                         except:
    310                             rollback(using=db)
    311                             raise
    312                 return res
    313             finally:
    314                 leave_transaction_management(using=db)
    315         return wraps(func)(_commit_on_success)
    316 
    317     # Note that although the first argument is *called* `using`, it
    318     # may actually be a function; @autocommit and @autocommit('foo')
    319     # are both allowed forms.
    320     if using is None:
    321         using = DEFAULT_DB_ALIAS
    322     if callable(using):
    323         return inner_commit_on_success(using, DEFAULT_DB_ALIAS)
    324     return lambda func: inner_commit_on_success(func, using)
     336   
     337    return _transaction_func(entering, exiting, using)
    325338
    326339def commit_manually(using=None):
    327340    """
    def commit_manually(using=None): 
    330343    own -- it's up to the user to call the commit and rollback functions
    331344    themselves.
    332345    """
    333     def inner_commit_manually(func, db=None):
    334         def _commit_manually(*args, **kw):
    335             try:
    336                 enter_transaction_management(using=db)
    337                 managed(True, using=db)
    338                 return func(*args, **kw)
    339             finally:
    340                 leave_transaction_management(using=db)
    341 
    342         return wraps(func)(_commit_manually)
    343 
    344     # Note that although the first argument is *called* `using`, it
    345     # may actually be a function; @autocommit and @autocommit('foo')
    346     # are both allowed forms.
    347     if using is None:
    348         using = DEFAULT_DB_ALIAS
    349     if callable(using):
    350         return inner_commit_manually(using, DEFAULT_DB_ALIAS)
    351     return lambda func: inner_commit_manually(func, using)
     346    def entering(using):
     347        enter_transaction_management(using=using)
     348        managed(True, using=using)
     349   
     350    def exiting(exc_value, using):
     351        leave_transaction_management(using=using)
     352   
     353    return _transaction_func(entering, exiting, using)
  • new file docs/releases/1.3.txt

    diff --git a/docs/releases/1.3.txt b/docs/releases/1.3.txt
    new file mode 100644
    index 0000000..2a93429
    - +  
     1.. _releases-1.3:
     2
     3========================
     4Django 1.3 release notes
     5========================
     6
     7*TBD.*
     8
     9Welcome to Django 1.3!
     10
     11New Features
     12============
     13
     14Transactions context managers
     15~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     16
     17Transactions functions can now be used as a context manager, with Python 2.5
     18and above.  For example:
     19   
     20    with transaction.autocommit():
     21        # ...
  • docs/topics/db/transactions.txt

    diff --git a/docs/topics/db/transactions.txt b/docs/topics/db/transactions.txt
    index fb6e3f8..0e5db80 100644
    a b parameter which should be the alias for a database connection for which the 
    6363behavior applies to.  If no alias is specified then the ``"default"`` database
    6464is used.
    6565
     66In addition, all of these decorators can be used as a context manager with
     67Python 2.5 and above.
     68
    6669.. note::
    6770
    6871    Although the examples below use view functions as examples, these
  • new file tests/modeltests/transactions/tests.py

    diff --git a/tests/modeltests/transactions/tests.py b/tests/modeltests/transactions/tests.py
    new file mode 100644
    index 0000000..ea7f072
    - +  
     1import sys
     2
     3from django.db import transaction
     4from django.test import TransactionTestCase
     5
     6from models import Reporter
     7
     8
     9class TransactionsTestCase(TransactionTestCase):
     10    def test_context_manager(self):
     11        tran = transaction.autocommit()
     12        tran.__enter__()
     13        try:
     14            Reporter.objects.create(first_name="Bob", last_name="Woodward",
     15                email="bob.woodward@deepthroat.net")
     16        finally:
     17            tran.__exit__(*sys.exc_info())
     18       
     19        self.assertEqual(Reporter.objects.count(), 1)
Back to Top