Ticket #9964: 9964-against-10087.diff

File 9964-against-10087.diff, 35.3 KB (added by shai, 6 years ago)

Improved patch, tested (tests included)

  • django/db/transaction/fast_select.py

     
    1010
    1111Managed transactions don't do those commits, but will need some kind of manual
    1212or implicit commits or rollbacks.
     13
     14This is a version of the transaction manager which marks transactions
     15as 'clean' or 'dirty', and neglects to close 'clean' transactions to
     16improve performnce.
     17
     18TODO: Explain when this breaks, and when it is less efficient
    1319"""
    1420
     21
    1522try:
    1623    import thread
    1724except ImportError:
    1825    import dummy_thread as thread
    19 try:
    20     from functools import wraps
    21 except ImportError:
    22     from django.utils.functional import wraps  # Python 2.3, 2.4 fallback.
     26
    2327from django.db import connection
    2428from django.conf import settings
    2529
    26 class TransactionManagementError(Exception):
    27     """
    28     This exception is thrown when something bad happens with transaction
    29     management.
    30     """
    31     pass
     30from django.db.transaction import TransactionManagementError
     31from django.db.transaction import state, savepoint_state
     32from django.db.transaction import is_managed, clean_savepoints, rollback
    3233
    33 # The states are dictionaries of lists. The key to the dict is the current
    34 # thread and the list is handled as a stack of values.
    35 state = {}
    36 savepoint_state = {}
    37 
    3834# The dirty flag is set by *_unless_managed functions to denote that the
    3935# code under transaction management has changed things to require a
    4036# database commit.
     
    109105        raise TransactionManagementError("This code isn't under transaction management")
    110106    clean_savepoints()
    111107
    112 def clean_savepoints():
    113     thread_ident = thread.get_ident()
    114     if thread_ident in savepoint_state:
    115         del savepoint_state[thread_ident]
    116 
    117 def is_managed():
    118     """
    119     Checks whether the transaction manager is in manual or in auto state.
    120     """
    121     thread_ident = thread.get_ident()
    122     if thread_ident in state:
    123         if state[thread_ident]:
    124             return state[thread_ident][-1]
    125     return settings.TRANSACTIONS_MANAGED
    126 
    127 def managed(flag=True):
    128     """
    129     Puts the transaction manager into a manual state: managed transactions have
    130     to be committed explicitly by the user. If you switch off transaction
    131     management and there is a pending commit/rollback, the data will be
    132     commited.
    133     """
    134     thread_ident = thread.get_ident()
    135     top = state.get(thread_ident, None)
    136     if top:
    137         top[-1] = flag
    138         if not flag and is_dirty():
    139             connection._commit()
    140             set_clean()
    141     else:
    142         raise TransactionManagementError("This code isn't under transaction management")
    143 
    144 def commit_unless_managed():
    145     """
    146     Commits changes if the system is not in managed transaction mode.
    147     """
    148     if not is_managed():
    149         connection._commit()
    150         clean_savepoints()
    151     else:
    152         set_dirty()
    153 
    154 def rollback_unless_managed():
    155     """
    156     Rolls back changes if the system is not in managed transaction mode.
    157     """
    158     if not is_managed():
    159         connection._rollback()
    160     else:
    161         set_dirty()
    162 
    163 def commit():
    164     """
    165     Does the commit itself and resets the dirty flag.
    166     """
    167     connection._commit()
    168     set_clean()
    169 
    170 def rollback():
    171     """
    172     This function does the rollback itself and resets the dirty flag.
    173     """
    174     connection._rollback()
    175     set_clean()
    176 
    177 def savepoint():
    178     """
    179     Creates a savepoint (if supported and required by the backend) inside the
    180     current transaction. Returns an identifier for the savepoint that will be
    181     used for the subsequent rollback or commit.
    182     """
    183     thread_ident = thread.get_ident()
    184     if thread_ident in savepoint_state:
    185         savepoint_state[thread_ident].append(None)
    186     else:
    187         savepoint_state[thread_ident] = [None]
    188     tid = str(thread_ident).replace('-', '')
    189     sid = "s%s_x%d" % (tid, len(savepoint_state[thread_ident]))
    190     connection._savepoint(sid)
    191     return sid
    192 
    193 def savepoint_rollback(sid):
    194     """
    195     Rolls back the most recent savepoint (if one exists). Does nothing if
    196     savepoints are not supported.
    197     """
    198     if thread.get_ident() in savepoint_state:
    199         connection._savepoint_rollback(sid)
    200 
    201 def savepoint_commit(sid):
    202     """
    203     Commits the most recent savepoint (if one exists). Does nothing if
    204     savepoints are not supported.
    205     """
    206     if thread.get_ident() in savepoint_state:
    207         connection._savepoint_commit(sid)
    208 
    209 ##############
    210 # DECORATORS #
    211 ##############
    212 
    213 def autocommit(func):
    214     """
    215     Decorator that activates commit on save. This is Django's default behavior;
    216     this decorator is useful if you globally activated transaction management in
    217     your settings file and want the default behavior in some view functions.
    218     """
    219     def _autocommit(*args, **kw):
    220         try:
    221             enter_transaction_management(managed=False)
    222             managed(False)
    223             return func(*args, **kw)
    224         finally:
    225             leave_transaction_management()
    226     return wraps(func)(_autocommit)
    227 
    228 def commit_on_success(func):
    229     """
    230     This decorator activates commit on response. This way, if the view function
    231     runs successfully, a commit is made; if the viewfunc produces an exception,
    232     a rollback is made. This is one of the most common ways to do transaction
    233     control in web apps.
    234     """
    235     def _commit_on_success(*args, **kw):
    236         try:
    237             enter_transaction_management()
    238             managed(True)
    239             try:
    240                 res = func(*args, **kw)
    241             except:
    242                 # All exceptions must be handled here (even string ones).
    243                 if is_dirty():
    244                     rollback()
    245                 raise
    246             else:
    247                 if is_dirty():
    248                     commit()
    249             return res
    250         finally:
    251             leave_transaction_management()
    252     return wraps(func)(_commit_on_success)
    253 
    254 def commit_manually(func):
    255     """
    256     Decorator that activates manual transaction control. It just disables
    257     automatic transaction control and doesn't do any commit/rollback of its
    258     own -- it's up to the user to call the commit and rollback functions
    259     themselves.
    260     """
    261     def _commit_manually(*args, **kw):
    262         try:
    263             enter_transaction_management()
    264             managed(True)
    265             return func(*args, **kw)
    266         finally:
    267             leave_transaction_management()
    268 
    269     return wraps(func)(_commit_manually)
  • django/db/transaction/safe.py

     
     1"""
     2This module implements a transaction manager that can be used to define
     3transaction handling in a request or view function. It is used by transaction
     4control middleware and decorators.
     5
     6The transaction manager can be in managed or in auto state. Auto state means the
     7system is using a commit-on-save strategy (actually it's more like
     8commit-on-change). As soon as the .save() or .delete() (or related) methods are
     9called, a commit is made.
     10
     11Managed transactions don't do those commits, but will need some kind of manual
     12or implicit commits or rollbacks.
     13
     14This is a version of the transaction manager which treats all transactions
     15as 'dirty', so that they are always closed.
     16"""
     17
     18try:
     19    import thread
     20except ImportError:
     21    import dummy_thread as thread
     22try:
     23    from functools import wraps
     24except ImportError:
     25    from django.utils.functional import wraps  # Python 2.3, 2.4 fallback.
     26from django.db import connection
     27from django.conf import settings
     28
     29from django.db.transaction import TransactionManagementError
     30from django.db.transaction import state, savepoint_state
     31from django.db.transaction import is_managed, clean_savepoints, rollback
     32
     33
     34def enter_transaction_management(managed=True):
     35    """
     36    Enters transaction management for a running thread. It must be balanced with
     37    the appropriate leave_transaction_management call, since the actual state is
     38    managed as a stack.
     39
     40    The state and dirty flag are carried over from the surrounding block or
     41    from the settings, if there is no surrounding block (dirty is always false
     42    when no current block is running).
     43    """
     44    thread_ident = thread.get_ident()
     45    if thread_ident in state and state[thread_ident]:
     46        state[thread_ident].append(state[thread_ident][-1])
     47    else:
     48        state[thread_ident] = []
     49        state[thread_ident].append(settings.TRANSACTIONS_MANAGED)
     50    connection._enter_transaction_management(managed)
     51
     52def leave_transaction_management():
     53    """
     54    Leaves transaction management for a running thread.
     55    """
     56    connection._leave_transaction_management(is_managed())
     57    thread_ident = thread.get_ident()
     58    if thread_ident in state and state[thread_ident]:
     59        del state[thread_ident][-1]
     60    else:
     61        raise TransactionManagementError("This code isn't under transaction management")
     62    # TODO: This has two drawbacks:
     63    #  - It generates a redundant database hit in most cases
     64    #  - As we no longer tell whether a transaction is dirty,
     65    #    we cannot throw exceptions if a transaction is pending
     66    # To go on the safe side, anything not committed should be rolled back
     67    rollback()
     68
     69def is_dirty():
     70    """
     71    All transactions are always dirty
     72    """
     73    return True
     74
     75def set_dirty():
     76    """
     77    All transactions are always dirty
     78    """
     79    pass
     80
     81def set_clean():
     82    """
     83    All transactions are always dirty
     84    """
     85    # TODO: Really? Retained from the fast_select version
     86    clean_savepoints()
     87
     88
     89
     90
  • django/db/transaction/__init__.py

     
     1"""
     2This module supports selection, by a setting, of a
     3transaction management "policy". Current policies
     4are "fast-select" (which leaves clean transactions
     5open) and "safe" (which makes sure all transactions
     6are closed, whether they are committed or rolled back.
     7
     8TODO: Document when fast is not safe, and when fast is
     9not even fast (repatable-read w/out MVCC).
     10"""
     11
     12###############
     13# Common base #
     14###############
     15
     16# These are common to the policies
     17try:
     18    import thread
     19except ImportError:
     20    import dummy_thread as thread
     21try:
     22    from functools import wraps
     23except ImportError:
     24    from django.utils.functional import wraps  # Python 2.3, 2.4 fallback.
     25from django.db import connection
     26from django.conf import settings
     27from django.core.exceptions import ImproperlyConfigured
     28
     29class TransactionManagementError(Exception):
     30    """
     31    This exception is thrown when something bad happens with transaction
     32    management.
     33    """
     34    pass
     35
     36# The states are dictionaries of lists. The key to the dict is the current
     37# thread and the list is handled as a stack of values.
     38state = {}
     39savepoint_state = {}
     40
     41def is_managed():
     42    """
     43    Checks whether the transaction manager is in manual or in auto state.
     44    Note: True means 'managed manually', False means 'auto'
     45    """
     46    thread_ident = thread.get_ident()
     47    if thread_ident in state:
     48        if state[thread_ident]:
     49            return state[thread_ident][-1]
     50    return settings.TRANSACTIONS_MANAGED
     51
     52def managed(flag=True):
     53    """
     54    Puts the transaction manager into a manual state: managed transactions have
     55    to be committed explicitly by the user. If you switch off transaction
     56    management and there is a pending commit/rollback, the data will be
     57    commited.
     58    """
     59    thread_ident = thread.get_ident()
     60    top = state.get(thread_ident, None)
     61    if top:
     62        top[-1] = flag
     63        if not flag and is_dirty():
     64            connection._commit()
     65            set_clean()
     66    else:
     67        raise TransactionManagementError("This code isn't under transaction management")
     68
     69def commit_unless_managed():
     70    """
     71    Commits changes if the system is not in managed transaction mode.
     72    """
     73    if not is_managed():
     74        connection._commit()
     75        clean_savepoints()
     76    else:
     77        set_dirty()
     78
     79def rollback_unless_managed():
     80    """
     81    Rolls back changes if the system is not in managed transaction mode.
     82    """
     83    if not is_managed():
     84        connection._rollback()
     85    else:
     86        set_dirty()
     87
     88def commit():
     89    """
     90    Does the commit itself and resets the dirty flag.
     91    """
     92    connection._commit()
     93    set_clean()
     94
     95def rollback():
     96    """
     97    This function does the rollback itself and resets the dirty flag.
     98    """
     99    connection._rollback()
     100    set_clean()
     101
     102def clean_savepoints():
     103    thread_ident = thread.get_ident()
     104    if thread_ident in savepoint_state:
     105        del savepoint_state[thread_ident]
     106
     107def savepoint():
     108    """
     109    Creates a savepoint (if supported and required by the backend) inside the
     110    current transaction. Returns an identifier for the savepoint that will be
     111    used for the subsequent rollback or commit.
     112    """
     113    thread_ident = thread.get_ident()
     114    if thread_ident in savepoint_state:
     115        savepoint_state[thread_ident].append(None)
     116    else:
     117        savepoint_state[thread_ident] = [None]
     118    tid = str(thread_ident).replace('-', '')
     119    sid = "s%s_x%d" % (tid, len(savepoint_state[thread_ident]))
     120    connection._savepoint(sid)
     121    return sid
     122
     123def savepoint_rollback(sid):
     124    """
     125    Rolls back the most recent savepoint (if one exists). Does nothing if
     126    savepoints are not supported.
     127    """
     128    if thread.get_ident() in savepoint_state:
     129        connection._savepoint_rollback(sid)
     130
     131def savepoint_commit(sid):
     132    """
     133    Commits the most recent savepoint (if one exists). Does nothing if
     134    savepoints are not supported.
     135    """
     136    if thread.get_ident() in savepoint_state:
     137        connection._savepoint_commit(sid)
     138
     139#####################
     140# Policy controlled #
     141#####################
     142
     143## DRY-minimizing set of functions for policy control
     144
     145def _get_policy_module():
     146    policy = getattr(settings, 'TRANSACTION_POLICY', 'fast_select')
     147    if policy=='fast_select':
     148        import fast_select
     149        return fast_select
     150    elif policy=='safe':
     151        import safe
     152        return safe
     153    else:
     154        raise ImproperlyConfigured, "Unknown transaction policy '%s'" % str(policy)
     155
     156_to_be_loaded = []
     157
     158def _load_from_policy_module():
     159    module = _get_policy_module()
     160    for name in _to_be_loaded:
     161        globals()[name] = getattr(module, name[1:])
     162
     163
     164def policy_controlled(func):
     165    """
     166    For a function "func", defines a function "_func" which, when
     167    first called, replaces itself with function "func" from the
     168    policy module. Thus, the implementation of func here can
     169    just be written:
     170    def func()
     171        return _func()
     172    policy_controlled(func)
     173    """
     174    place_holder_name = "_" + func.func_name
     175    def place_holder_func(*args, **kwargs):
     176        """
     177        This code is only executed on the first place_holder func to be called
     178        """
     179        #print "policy_controlled, replacing ",place_holder_name
     180        _load_from_policy_module()
     181        real_func = globals()[place_holder_name]
     182        return real_func(*args, **kwargs)
     183    globals()[place_holder_name] = place_holder_func
     184    _to_be_loaded.append(place_holder_name)
     185    return func
     186
     187# The policy controlled functions
     188
     189def enter_transaction_management(managed=True):
     190    return _enter_transaction_management(managed)
     191policy_controlled(enter_transaction_management)
     192
     193def leave_transaction_management():
     194    return _leave_transaction_management()
     195policy_controlled(leave_transaction_management)
     196
     197def is_dirty():
     198    return _is_dirty()
     199policy_controlled(is_dirty)
     200
     201def set_dirty():
     202    return _set_dirty()
     203policy_controlled(set_dirty)
     204
     205def set_clean():
     206    return _set_clean()
     207policy_controlled(set_clean)
     208
     209##############
     210# DECORATORS #
     211##############
     212
     213# Decorators use the above functions, and their own code
     214# doesn't need to change to reflect policy; they are
     215# defined here to make sure they reference the right versions
     216# of the functions they use
     217
     218def autocommit(func):
     219    """
     220    Decorator that activates commit on save. This is Django's default behavior;
     221    this decorator is useful if you globally activated transaction management in
     222    your settings file and want the default behavior in some view functions.
     223    """
     224    def _autocommit(*args, **kw):
     225        try:
     226            enter_transaction_management(managed=False)
     227            managed(False)
     228            return func(*args, **kw)
     229        finally:
     230            leave_transaction_management()
     231    return wraps(func)(_autocommit)
     232
     233def commit_on_success(func):
     234    """
     235    This decorator activates commit on response. This way, if the view function
     236    runs successfully, a commit is made; if the viewfunc produces an exception,
     237    a rollback is made. This is one of the most common ways to do transaction
     238    control in web apps.
     239    """
     240    def _commit_on_success(*args, **kw):
     241        try:
     242            enter_transaction_management()
     243            managed(True)
     244            try:
     245                res = func(*args, **kw)
     246            except:
     247                # All exceptions must be handled here (even string ones).
     248                if is_dirty():
     249                    rollback()
     250                raise
     251            else:
     252                if is_dirty():
     253                    commit()
     254            return res
     255        finally:
     256            leave_transaction_management()
     257    return wraps(func)(_commit_on_success)
     258
     259def commit_manually(func):
     260    """
     261    Decorator that activates manual transaction control. It just disables
     262    automatic transaction control and doesn't do any commit/rollback of its
     263    own -- it's up to the user to call the commit and rollback functions
     264    themselves.
     265    """
     266    def _commit_manually(*args, **kw):
     267        try:
     268            enter_transaction_management()
     269            managed(True)
     270            return func(*args, **kw)
     271        finally:
     272            leave_transaction_management()
     273
     274    return wraps(func)(_commit_manually)
  • django/db/transaction.py

     
    1 """
    2 This module implements a transaction manager that can be used to define
    3 transaction handling in a request or view function. It is used by transaction
    4 control middleware and decorators.
    5 
    6 The transaction manager can be in managed or in auto state. Auto state means the
    7 system is using a commit-on-save strategy (actually it's more like
    8 commit-on-change). As soon as the .save() or .delete() (or related) methods are
    9 called, a commit is made.
    10 
    11 Managed transactions don't do those commits, but will need some kind of manual
    12 or implicit commits or rollbacks.
    13 """
    14 
    15 try:
    16     import thread
    17 except ImportError:
    18     import dummy_thread as thread
    19 try:
    20     from functools import wraps
    21 except ImportError:
    22     from django.utils.functional import wraps  # Python 2.3, 2.4 fallback.
    23 from django.db import connection
    24 from django.conf import settings
    25 
    26 class TransactionManagementError(Exception):
    27     """
    28     This exception is thrown when something bad happens with transaction
    29     management.
    30     """
    31     pass
    32 
    33 # The states are dictionaries of lists. The key to the dict is the current
    34 # thread and the list is handled as a stack of values.
    35 state = {}
    36 savepoint_state = {}
    37 
    38 # The dirty flag is set by *_unless_managed functions to denote that the
    39 # code under transaction management has changed things to require a
    40 # database commit.
    41 dirty = {}
    42 
    43 def enter_transaction_management(managed=True):
    44     """
    45     Enters transaction management for a running thread. It must be balanced with
    46     the appropriate leave_transaction_management call, since the actual state is
    47     managed as a stack.
    48 
    49     The state and dirty flag are carried over from the surrounding block or
    50     from the settings, if there is no surrounding block (dirty is always false
    51     when no current block is running).
    52     """
    53     thread_ident = thread.get_ident()
    54     if thread_ident in state and state[thread_ident]:
    55         state[thread_ident].append(state[thread_ident][-1])
    56     else:
    57         state[thread_ident] = []
    58         state[thread_ident].append(settings.TRANSACTIONS_MANAGED)
    59     if thread_ident not in dirty:
    60         dirty[thread_ident] = False
    61     connection._enter_transaction_management(managed)
    62 
    63 def leave_transaction_management():
    64     """
    65     Leaves transaction management for a running thread. A dirty flag is carried
    66     over to the surrounding block, as a commit will commit all changes, even
    67     those from outside. (Commits are on connection level.)
    68     """
    69     connection._leave_transaction_management(is_managed())
    70     thread_ident = thread.get_ident()
    71     if thread_ident in state and state[thread_ident]:
    72         del state[thread_ident][-1]
    73     else:
    74         raise TransactionManagementError("This code isn't under transaction management")
    75     if dirty.get(thread_ident, False):
    76         rollback()
    77         raise TransactionManagementError("Transaction managed block ended with pending COMMIT/ROLLBACK")
    78     dirty[thread_ident] = False
    79 
    80 def is_dirty():
    81     """
    82     Returns True if the current transaction requires a commit for changes to
    83     happen.
    84     """
    85     return dirty.get(thread.get_ident(), False)
    86 
    87 def set_dirty():
    88     """
    89     Sets a dirty flag for the current thread and code streak. This can be used
    90     to decide in a managed block of code to decide whether there are open
    91     changes waiting for commit.
    92     """
    93     thread_ident = thread.get_ident()
    94     if thread_ident in dirty:
    95         dirty[thread_ident] = True
    96     else:
    97         raise TransactionManagementError("This code isn't under transaction management")
    98 
    99 def set_clean():
    100     """
    101     Resets a dirty flag for the current thread and code streak. This can be used
    102     to decide in a managed block of code to decide whether a commit or rollback
    103     should happen.
    104     """
    105     thread_ident = thread.get_ident()
    106     if thread_ident in dirty:
    107         dirty[thread_ident] = False
    108     else:
    109         raise TransactionManagementError("This code isn't under transaction management")
    110     clean_savepoints()
    111 
    112 def clean_savepoints():
    113     thread_ident = thread.get_ident()
    114     if thread_ident in savepoint_state:
    115         del savepoint_state[thread_ident]
    116 
    117 def is_managed():
    118     """
    119     Checks whether the transaction manager is in manual or in auto state.
    120     """
    121     thread_ident = thread.get_ident()
    122     if thread_ident in state:
    123         if state[thread_ident]:
    124             return state[thread_ident][-1]
    125     return settings.TRANSACTIONS_MANAGED
    126 
    127 def managed(flag=True):
    128     """
    129     Puts the transaction manager into a manual state: managed transactions have
    130     to be committed explicitly by the user. If you switch off transaction
    131     management and there is a pending commit/rollback, the data will be
    132     commited.
    133     """
    134     thread_ident = thread.get_ident()
    135     top = state.get(thread_ident, None)
    136     if top:
    137         top[-1] = flag
    138         if not flag and is_dirty():
    139             connection._commit()
    140             set_clean()
    141     else:
    142         raise TransactionManagementError("This code isn't under transaction management")
    143 
    144 def commit_unless_managed():
    145     """
    146     Commits changes if the system is not in managed transaction mode.
    147     """
    148     if not is_managed():
    149         connection._commit()
    150         clean_savepoints()
    151     else:
    152         set_dirty()
    153 
    154 def rollback_unless_managed():
    155     """
    156     Rolls back changes if the system is not in managed transaction mode.
    157     """
    158     if not is_managed():
    159         connection._rollback()
    160     else:
    161         set_dirty()
    162 
    163 def commit():
    164     """
    165     Does the commit itself and resets the dirty flag.
    166     """
    167     connection._commit()
    168     set_clean()
    169 
    170 def rollback():
    171     """
    172     This function does the rollback itself and resets the dirty flag.
    173     """
    174     connection._rollback()
    175     set_clean()
    176 
    177 def savepoint():
    178     """
    179     Creates a savepoint (if supported and required by the backend) inside the
    180     current transaction. Returns an identifier for the savepoint that will be
    181     used for the subsequent rollback or commit.
    182     """
    183     thread_ident = thread.get_ident()
    184     if thread_ident in savepoint_state:
    185         savepoint_state[thread_ident].append(None)
    186     else:
    187         savepoint_state[thread_ident] = [None]
    188     tid = str(thread_ident).replace('-', '')
    189     sid = "s%s_x%d" % (tid, len(savepoint_state[thread_ident]))
    190     connection._savepoint(sid)
    191     return sid
    192 
    193 def savepoint_rollback(sid):
    194     """
    195     Rolls back the most recent savepoint (if one exists). Does nothing if
    196     savepoints are not supported.
    197     """
    198     if thread.get_ident() in savepoint_state:
    199         connection._savepoint_rollback(sid)
    200 
    201 def savepoint_commit(sid):
    202     """
    203     Commits the most recent savepoint (if one exists). Does nothing if
    204     savepoints are not supported.
    205     """
    206     if thread.get_ident() in savepoint_state:
    207         connection._savepoint_commit(sid)
    208 
    209 ##############
    210 # DECORATORS #
    211 ##############
    212 
    213 def autocommit(func):
    214     """
    215     Decorator that activates commit on save. This is Django's default behavior;
    216     this decorator is useful if you globally activated transaction management in
    217     your settings file and want the default behavior in some view functions.
    218     """
    219     def _autocommit(*args, **kw):
    220         try:
    221             enter_transaction_management(managed=False)
    222             managed(False)
    223             return func(*args, **kw)
    224         finally:
    225             leave_transaction_management()
    226     return wraps(func)(_autocommit)
    227 
    228 def commit_on_success(func):
    229     """
    230     This decorator activates commit on response. This way, if the view function
    231     runs successfully, a commit is made; if the viewfunc produces an exception,
    232     a rollback is made. This is one of the most common ways to do transaction
    233     control in web apps.
    234     """
    235     def _commit_on_success(*args, **kw):
    236         try:
    237             enter_transaction_management()
    238             managed(True)
    239             try:
    240                 res = func(*args, **kw)
    241             except:
    242                 # All exceptions must be handled here (even string ones).
    243                 if is_dirty():
    244                     rollback()
    245                 raise
    246             else:
    247                 if is_dirty():
    248                     commit()
    249             return res
    250         finally:
    251             leave_transaction_management()
    252     return wraps(func)(_commit_on_success)
    253 
    254 def commit_manually(func):
    255     """
    256     Decorator that activates manual transaction control. It just disables
    257     automatic transaction control and doesn't do any commit/rollback of its
    258     own -- it's up to the user to call the commit and rollback functions
    259     themselves.
    260     """
    261     def _commit_manually(*args, **kw):
    262         try:
    263             enter_transaction_management()
    264             managed(True)
    265             return func(*args, **kw)
    266         finally:
    267             leave_transaction_management()
    268 
    269     return wraps(func)(_commit_manually)
  • django/conf/project_template/settings.py

     
    1616DATABASE_HOST = ''             # Set to empty string for localhost. Not used with sqlite3.
    1717DATABASE_PORT = ''             # Set to empty string for default. Not used with sqlite3.
    1818
     19
     20TRANSACTION_POLICY = 'safe' # Options: 'safe','fast_select'; TODO: better doc.
     21
    1922# Local time zone for this installation. Choices can be found here:
    2023# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
    2124# although not all choices may be available on all operating systems.
  • django/conf/global_settings.py

     
    131131DATABASE_PORT = ''             # Set to empty string for default. Not used with sqlite3.
    132132DATABASE_OPTIONS = {}          # Set to empty dictionary for default.
    133133
     134
    134135# Host for sending e-mail.
    135136EMAIL_HOST = 'localhost'
    136137
     
    282283
    283284# Do you want to manage transactions manually?
    284285# Hint: you really don't!
    285 TRANSACTIONS_MANAGED = False
     286TRANSACTIONS_MANAGED = False # False means 'automatic'
    286287
     288# Django's policy for managed database transactions.
     289# The current options are:
     290#  'safe': always end transaction at the end of request processing.
     291#  'fast_select': leave "clean" transactions open for reuse; pre-1.1 behavior.
     292TRANSACTION_POLICY = 'fast_select'
     293
    287294# The User-Agent string to use when checking for URL validity through the
    288295# isExistingURL validator.
    289296from django import get_version
  • tests/modeltests/transactions/models.py

     
    8686>>> Reporter.objects.all()
    8787[<Reporter: Ben Jones>, <Reporter: Carol Doe>]
    8888
    89 # If you forget, you'll get bad errors
     89# If you forget, you'll get errors or rollbacks, depending on transaction policy
    9090>>> def manually_managed_mistake():
    9191...     r = Reporter(first_name="David", last_name="Davidson")
    9292...     r.save()
    9393...     # oops, I forgot to commit/rollback!
    9494>>> manually_managed_mistake = transaction.commit_manually(manually_managed_mistake)
    95 >>> manually_managed_mistake()
     95>>> manually_managed_mistake()"""
     96
     97# Note: The above string must not include a final newline!
     98
     99if getattr(settings, 'TRANSACTION_POLICY', 'fast_select')!='safe':
     100    __test__['API_TESTS'] += """
    96101Traceback (most recent call last):
    97102    ...
    98103TransactionManagementError: Transaction managed block ended with pending COMMIT/ROLLBACK
    99104"""
     105else:
     106    __test__['API_TESTS'] += """
     107>>> Reporter.objects.all()
     108[<Reporter: Ben Jones>, <Reporter: Carol Doe>]
     109
     110"""
  • tests/modeltests/transaction_middleware/__init__.py

     
     1
  • tests/modeltests/transaction_middleware/models.py

     
    11"""
    2 15. Transactions
     215m. Transaction Middleware
    33
    4 Django handles transactions in three different ways. The default is to commit
    5 each transaction upon a write, but you can decorate a function to get
    6 commit-on-success behavior. Alternatively, you can manage the transaction
    7 manually.
     4Django's Transaction Middleware offers a simple consistent way
     5to wrap every request in a transaction. The behavior is supposed
     6to be equivalent to commit_on_success applied to the entire request
     7processing (within the scope of the middleware, of course).
     8These tests are closely related to the transaction tests, of course.
    89"""
    910
    1011from django.db import models
     
    1819        return u"%s %s" % (self.first_name, self.last_name)
    1920
    2021__test__ = {'API_TESTS':"""
     22>>> from django.middleware.transaction import TransactionMiddleware
     23>>> from django.utils.decorators import decorator_from_middleware
     24>>> xact_middle = decorator_from_middleware(TransactionMiddleware)
    2125>>> from django.db import connection, transaction
    2226"""}
    2327
     
    5357>>> Reporter.objects.all()
    5458[<Reporter: Alice Smith>, <Reporter: Ben Jones>]
    5559
    56 # With the commit_on_success decorator, the transaction is only comitted if the
     60# With the transaction middleware (used here as a decorator),
     61# the transaction is only comitted if the
    5762# function doesn't throw an exception
    58 >>> committed_on_success = transaction.commit_on_success(create_a_reporter_then_fail)
     63>>> committed_on_success = xact_middle(create_a_reporter_then_fail)
    5964>>> committed_on_success("Carol", "Doe")
    6065Traceback (most recent call last):
    6166    ...
     
    6671[<Reporter: Alice Smith>, <Reporter: Ben Jones>]
    6772
    6873# If there aren't any exceptions, the data will get saved
    69 >>> def remove_a_reporter():
    70 ...     r = Reporter.objects.get(first_name="Alice")
     74>>> def remove_a_reporter(first):
     75...     r = Reporter.objects.get(first_name=first)
    7176...     r.delete()
    7277...
    73 >>> remove_comitted_on_success = transaction.commit_on_success(remove_a_reporter)
    74 >>> remove_comitted_on_success()
     78>>> remove_comitted_on_success = xact_middle(remove_a_reporter)
     79>>> remove_comitted_on_success("Alice")
    7580>>> Reporter.objects.all()
    7681[<Reporter: Ben Jones>]
     82"""
     83if not getattr(settings, 'TRANSACTION_POLICY', None)=='safe':
     84    __test__['API_TESTS'] += """
     85# Note there is a trick with custom sql (in fast_select): Executing it doesn't
     86# set the transaction 'dirty', so it may not be committed
     87>>> def remove_all_reporters(ignored_request):
     88...     cursor = connection.cursor()
     89...     sql = 'delete from ' + Reporter._meta.db_table
     90...     cursor.execute(sql)
     91...
     92>>> remove_all_no_commit = xact_middle(remove_all_reporters)
     93>>> remove_all_no_commit(None)
     94>>> Reporter.objects.all()
     95[]
    7796
    78 # You can manually manage transactions if you really want to, but you
    79 # have to remember to commit/rollback
    80 >>> def manually_managed():
    81 ...     r = Reporter(first_name="Carol", last_name="Doe")
    82 ...     r.save()
    83 ...     transaction.commit()
    84 >>> manually_managed = transaction.commit_manually(manually_managed)
    85 >>> manually_managed()
     97>>> transaction.rollback()
    8698>>> Reporter.objects.all()
    87 [<Reporter: Ben Jones>, <Reporter: Carol Doe>]
     99[<Reporter: Ben Jones>]
    88100
    89 # If you forget, you'll get bad errors
    90 >>> def manually_managed_mistake():
    91 ...     r = Reporter(first_name="David", last_name="Davidson")
    92 ...     r.save()
    93 ...     # oops, I forgot to commit/rollback!
    94 >>> manually_managed_mistake = transaction.commit_manually(manually_managed_mistake)
    95 >>> manually_managed_mistake()
    96 Traceback (most recent call last):
    97     ...
    98 TransactionManagementError: Transaction managed block ended with pending COMMIT/ROLLBACK
     101# To do it properly, you need to set the transaction dirty
     102# when you execute raw SQL
     103# set the transaction 'dirty', so it may not be committed
     104>>> def remove_all_reporters_dirty(ignored_request):
     105...     cursor = connection.cursor()
     106...     sql = 'delete from ' + Reporter._meta.db_table
     107...     cursor.execute(sql)
     108...     transaction.set_dirty()
     109...
     110>>> remove_all_commit = xact_middle(remove_all_reporters_dirty)
     111>>> remove_all_commit(None)
     112>>> Reporter.objects.all()
     113[]
     114
     115>>> transaction.rollback()
     116>>> Reporter.objects.all()
     117[]
    99118"""
     119else:
     120    __test__['API_TESTS'] += """
     121# Under a 'safe' transaction policy, there's no need to set_dirty
     122>>> def remove_all_reporters(ignored_request):
     123...     cursor = connection.cursor()
     124...     sql = 'delete from ' + Reporter._meta.db_table
     125...     cursor.execute(sql)
     126...
     127>>> remove_all_safe = xact_middle(remove_all_reporters)
     128
     129# See that we have a reporter:
     130>>> Reporter.objects.all()
     131[<Reporter: Ben Jones>]
     132
     133>>> remove_all_safe(None)
     134
     135# Now we don't, and rollback doesn't get it back
     136>>> Reporter.objects.all()
     137[]
     138>>> transaction.rollback()
     139>>> Reporter.objects.all()
     140[]
     141
     142"""
Back to Top