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) |