Kode sumber untuk django.db.transaction

[docs]classTransactionManagementError(ProgrammingError):""" This exception is thrown when transaction management is used improperly. """pass

defget_connection(using=None):""" Get a database connection by name, or the default database connection if no name is provided. This is a private API. """ifusingisNone:using=DEFAULT_DB_ALIASreturnconnections[using]

[docs]defget_autocommit(using=None):""" Get the autocommit status of the connection. """returnget_connection(using).get_autocommit()

[docs]defset_autocommit(autocommit,using=None):""" Set the autocommit status of the connection. """returnget_connection(using).set_autocommit(autocommit)

[docs]defrollback(using=None):""" Rolls back a transaction. """get_connection(using).rollback()

[docs]defsavepoint(using=None):""" Creates a savepoint (if supported and required by the backend) inside the current transaction. Returns an identifier for the savepoint that will be used for the subsequent rollback or commit. """returnget_connection(using).savepoint()

[docs]defsavepoint_rollback(sid,using=None):""" Rolls back the most recent savepoint (if one exists). Does nothing if savepoints are not supported. """get_connection(using).savepoint_rollback(sid)

[docs]defsavepoint_commit(sid,using=None):""" Commits the most recent savepoint (if one exists). Does nothing if savepoints are not supported. """get_connection(using).savepoint_commit(sid)

[docs]defclean_savepoints(using=None):""" Resets the counter used to generate unique savepoint ids in this thread. """get_connection(using).clean_savepoints()

[docs]defset_rollback(rollback,using=None):""" Sets or unsets the "needs rollback" flag -- for *advanced use* only. When `rollback` is `True`, it triggers a rollback when exiting the innermost enclosing atomic block that has `savepoint=True` (that's the default). Use this to force a rollback without raising an exception. When `rollback` is `False`, it prevents such a rollback. Use this only after rolling back to a known-good state! Otherwise, you break the atomic block and data corruption may occur. """returnget_connection(using).set_rollback(rollback)

[docs]defon_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)

################################## Decorators / context managers ##################################classAtomic(ContextDecorator):""" This class guarantees the atomic execution of a given block. An instance can be used either as a decorator or as a context manager. When it's used as a decorator, __call__ wraps the execution of the decorated function in the instance itself, used as a context manager. When it's used as a context manager, __enter__ creates a transaction or a savepoint, depending on whether a transaction is already in progress, and __exit__ commits the transaction or releases the savepoint on normal exit, and rolls back the transaction or to the savepoint on exceptions. It's possible to disable the creation of savepoints if the goal is to ensure that some code runs within a transaction without creating overhead. A stack of savepoints identifiers is maintained as an attribute of the connection. None denotes the absence of a savepoint. This allows reentrancy even if the same AtomicWrapper is reused. For example, it's possible to define `oa = @atomic('other')` and use `@oa` or `with oa:` multiple times. Since database connections are thread-local, this is thread-safe. This is a private API. """def__init__(self,using,savepoint):self.using=usingself.savepoint=savepointdef__enter__(self):connection=get_connection(self.using)ifnotconnection.in_atomic_block:# Reset state when entering an outermost atomic block.connection.commit_on_exit=Trueconnection.needs_rollback=Falseifnotconnection.get_autocommit():# Some database adapters (namely sqlite3) don't handle# transactions and savepoints properly when autocommit is off.# Turning autocommit back on isn't an option; it would trigger# a premature commit. Give up if that happens.ifconnection.features.autocommits_when_autocommit_is_off:raiseTransactionManagementError("Your database backend doesn't behave properly when ""autocommit is off. Turn it on before using 'atomic'.")# Pretend we're already in an atomic block to bypass the code# that disables autocommit to enter a transaction, and make a# note to deal with this case in __exit__.connection.in_atomic_block=Trueconnection.commit_on_exit=Falseifconnection.in_atomic_block:# We're already in a transaction; create a savepoint, unless we# were told not to or we're already waiting for a rollback. The# second condition avoids creating useless savepoints and prevents# overwriting needs_rollback until the rollback is performed.ifself.savepointandnotconnection.needs_rollback:sid=connection.savepoint()connection.savepoint_ids.append(sid)else:connection.savepoint_ids.append(None)else:connection.set_autocommit(False,force_begin_transaction_with_broken_autocommit=True)connection.in_atomic_block=Truedef__exit__(self,exc_type,exc_value,traceback):connection=get_connection(self.using)ifconnection.savepoint_ids:sid=connection.savepoint_ids.pop()else:# Prematurely unset this flag to allow using commit or rollback.connection.in_atomic_block=Falsetry:ifconnection.closed_in_transaction:# The database will perform a rollback by itself.# Wait until we exit the outermost block.passelifexc_typeisNoneandnotconnection.needs_rollback:ifconnection.in_atomic_block:# Release savepoint if there is oneifsidisnotNone:try:connection.savepoint_commit(sid)exceptDatabaseError:try:connection.savepoint_rollback(sid)# The savepoint won't be reused. Release it to# minimize overhead for the database server.connection.savepoint_commit(sid)exceptError:# If rolling back to a savepoint fails, mark for# rollback at a higher level and avoid shadowing# the original exception.connection.needs_rollback=Trueraiseelse:# Commit transactiontry:connection.commit()exceptDatabaseError:try:connection.rollback()exceptError:# An error during rollback means that something# went wrong with the connection. Drop it.connection.close()raiseelse:# This flag will be set to True again if there isn't a savepoint# allowing to perform the rollback at this level.connection.needs_rollback=Falseifconnection.in_atomic_block:# Roll back to savepoint if there is one, mark for rollback# otherwise.ifsidisNone:connection.needs_rollback=Trueelse:try:connection.savepoint_rollback(sid)# The savepoint won't be reused. Release it to# minimize overhead for the database server.connection.savepoint_commit(sid)exceptError:# If rolling back to a savepoint fails, mark for# rollback at a higher level and avoid shadowing# the original exception.connection.needs_rollback=Trueelse:# Roll back transactiontry:connection.rollback()exceptError:# An error during rollback means that something# went wrong with the connection. Drop it.connection.close()finally:# Outermost block exit when autocommit was enabled.ifnotconnection.in_atomic_block:ifconnection.closed_in_transaction:connection.connection=Noneelse:connection.set_autocommit(True)# Outermost block exit when autocommit was disabled.elifnotconnection.savepoint_idsandnotconnection.commit_on_exit:ifconnection.closed_in_transaction:connection.connection=Noneelse:connection.in_atomic_block=False

[docs]defatomic(using=None,savepoint=True):# Bare decorator: @atomic -- although the first argument is called# `using`, it's actually the function being decorated.ifcallable(using):returnAtomic(DEFAULT_DB_ALIAS,savepoint)(using)# Decorator: @atomic(...) or context manager: with atomic(...): ...else:returnAtomic(using,savepoint)