diff --git a/django/db/transaction.py b/django/db/transaction.py
index 3c767f1..562fc49 100644
|
a
|
b
|
called, a commit is made.
|
| 11 | 11 | Managed transactions don't do those commits, but will need some kind of manual |
| 12 | 12 | or implicit commits or rollbacks. |
| 13 | 13 | """ |
| | 14 | import sys |
| 14 | 15 | |
| 15 | 16 | try: |
| 16 | 17 | import thread |
| … |
… |
try:
|
| 20 | 21 | from functools import wraps |
| 21 | 22 | except ImportError: |
| 22 | 23 | from django.utils.functional import wraps # Python 2.4 fallback. |
| 23 | | from django.db import connections, DEFAULT_DB_ALIAS |
| | 24 | |
| 24 | 25 | from django.conf import settings |
| | 26 | from django.db import connections, DEFAULT_DB_ALIAS |
| 25 | 27 | |
| 26 | 28 | class TransactionManagementError(Exception): |
| 27 | 29 | """ |
| … |
… |
def savepoint_commit(sid, using=None):
|
| 257 | 259 | # DECORATORS # |
| 258 | 260 | ############## |
| 259 | 261 | |
| 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): |
| | 262 | class 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__() |
| 268 | 278 | 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 |
| 275 | 287 | |
| | 288 | def _transaction_func(entering, exiting, using): |
| 276 | 289 | # Note that although the first argument is *called* `using`, it |
| 277 | 290 | # may actually be a function; @autocommit and @autocommit('foo') |
| 278 | 291 | # are both allowed forms. |
| 279 | 292 | if using is None: |
| 280 | 293 | using = DEFAULT_DB_ALIAS |
| 281 | 294 | 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) |
| 284 | 297 | |
| 285 | 298 | |
| | 299 | def 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 | |
| 286 | 314 | def commit_on_success(using=None): |
| 287 | 315 | """ |
| 288 | 316 | This decorator activates commit on response. This way, if the view function |
| … |
… |
def commit_on_success(using=None):
|
| 290 | 318 | a rollback is made. This is one of the most common ways to do transaction |
| 291 | 319 | control in web apps. |
| 292 | 320 | """ |
| 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): |
| 298 | 331 | try: |
| 299 | | res = func(*args, **kw) |
| | 332 | commit(using=using) |
| 300 | 333 | 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) |
| 304 | 335 | 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) |
| 325 | 338 | |
| 326 | 339 | def commit_manually(using=None): |
| 327 | 340 | """ |
| … |
… |
def commit_manually(using=None):
|
| 330 | 343 | own -- it's up to the user to call the commit and rollback functions |
| 331 | 344 | themselves. |
| 332 | 345 | """ |
| 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) |
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 | ======================== |
| | 4 | Django 1.3 release notes |
| | 5 | ======================== |
| | 6 | |
| | 7 | *TBD.* |
| | 8 | |
| | 9 | Welcome to Django 1.3! |
| | 10 | |
| | 11 | New Features |
| | 12 | ============ |
| | 13 | |
| | 14 | Transactions context managers |
| | 15 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| | 16 | |
| | 17 | Transactions functions can now be used as a context manager, with Python 2.5 |
| | 18 | and above. For example: |
| | 19 | |
| | 20 | with transaction.autocommit(): |
| | 21 | # ... |
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
|
| 63 | 63 | behavior applies to. If no alias is specified then the ``"default"`` database |
| 64 | 64 | is used. |
| 65 | 65 | |
| | 66 | In addition, all of these decorators can be used as a context manager with |
| | 67 | Python 2.5 and above. |
| | 68 | |
| 66 | 69 | .. note:: |
| 67 | 70 | |
| 68 | 71 | Although the examples below use view functions as examples, these |
diff --git a/tests/modeltests/transactions/tests.py b/tests/modeltests/transactions/tests.py
new file mode 100644
index 0000000..ea7f072
|
-
|
+
|
|
| | 1 | import sys |
| | 2 | |
| | 3 | from django.db import transaction |
| | 4 | from django.test import TransactionTestCase |
| | 5 | |
| | 6 | from models import Reporter |
| | 7 | |
| | 8 | |
| | 9 | class 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) |