Ticket #9964: 9964-r14913-deprecation.patch
File 9964-r14913-deprecation.patch, 18.9 KB (added by , 14 years ago) |
---|
-
django/db/backends/util.py
52 52 def __iter__(self): 53 53 return iter(self.cursor) 54 54 55 class CursorUseNotifyWrapper(object): 56 def __init__(self, cursor, on_use): 57 self.cursor = cursor 58 self.on_use = on_use 59 def __getattr__(self, attr): 60 self.on_use() 61 return getattr(self.cursor, attr) 62 def __iter__(self): 63 return iter(self.cursor) 64 55 65 ############################################### 56 66 # Converters from database (string) to Python # 57 67 ############################################### -
django/db/backends/__init__.py
22 22 self.settings_dict = settings_dict 23 23 self.alias = alias 24 24 self.use_debug_cursor = None 25 self.on_used = None 25 26 26 27 def __eq__(self, other): 27 28 return self.alias == other.alias … … 75 76 def cursor(self): 76 77 from django.conf import settings 77 78 cursor = self._cursor() 79 # Used by transaction mechanism to be notified that db was used 80 if self.on_used: 81 cursor = util.CursorUseNotifyWrapper(cursor, self.on_used) 78 82 if (self.use_debug_cursor or 79 83 (self.use_debug_cursor is None and settings.DEBUG)): 80 84 return self.make_debug_cursor(cursor) -
django/db/transaction.py
12 12 or implicit commits or rollbacks. 13 13 """ 14 14 import sys 15 from warnings import warn 15 16 16 17 try: 17 18 import thread … … 32 33 """ 33 34 pass 34 35 36 class ReadonlyTransactionWarning(PendingDeprecationWarning): 37 """ 38 This is a warning issued when there is a difference between current behavior 39 and future behavior due to read-only transactions being changed from clean 40 to dirty. 41 """ 42 pass 43 35 44 # The states are dictionaries of dictionaries of lists. The key to the outer 36 45 # dict is the current thread, and the key to the inner dictionary is the 37 46 # connection alias and the list is handled as a stack of values. 38 47 state = {} 39 48 savepoint_state = {} 40 49 41 # The dirty flag is set by *_unless_managed functionsto denote that the42 # code under transaction management has changedthings to require a50 # The dirty flag is set whenever a database cursor is touched to denote that the 51 # code under transaction management has done things to require a 43 52 # database commit. 44 53 # This is a dictionary mapping thread to a dictionary mapping connection 45 54 # alias to a boolean. 46 55 dirty = {} 56 # The deprecated_dirty flag is set by *_unless_managed functions. It is the old, 57 # slightly incorrect sense of dirty whereby read-only transactions may be left pending. 58 # This has the same structure as the dirty flag. 59 deprecated_dirty = {} 47 60 48 61 def enter_transaction_management(managed=True, using=None): 49 62 """ … … 67 80 if thread_ident not in dirty or using not in dirty[thread_ident]: 68 81 dirty.setdefault(thread_ident, {}) 69 82 dirty[thread_ident][using] = False 83 if thread_ident not in deprecated_dirty or using not in deprecated_dirty[thread_ident]: 84 deprecated_dirty.setdefault(thread_ident, {}) 85 deprecated_dirty[thread_ident][using] = False 86 def set_dirty_if_managed(): 87 if is_managed(using): _set_dirty(dirty, using) 88 connection.on_used = set_dirty_if_managed 70 89 connection._enter_transaction_management(managed) 71 90 72 91 def leave_transaction_management(using=None): … … 84 103 del state[thread_ident][using][-1] 85 104 else: 86 105 raise TransactionManagementError("This code isn't under transaction management") 87 if dirty.get(thread_ident, {}).get(using, False): 106 if deprecated_dirty.get(thread_ident, {}).get(using, False): 107 # The transaction is dirty in the strict sense. This has always been an error 88 108 rollback(using=using) 89 109 raise TransactionManagementError("Transaction managed block ended with pending COMMIT/ROLLBACK") 110 elif dirty.get(thread_ident, {}).get(using, False): 111 #rollback(using=using) 112 warn("Transaction managed block ended with pending COMMIT/ROLLBACK", ReadonlyTransactionWarning) 90 113 dirty[thread_ident][using] = False 91 114 115 def _is_dirty(dirty_dict, using=None): 116 """ 117 Returns True if the current transaction requires a commit to close. 118 """ 119 # TODO: Once deprecated_dirty is eliminated, fold this back into is_dirty 120 if using is None: 121 using = DEFAULT_DB_ALIAS 122 return dirty_dict.get(thread.get_ident(), {}).get(using, False) 123 92 124 def is_dirty(using=None): 93 125 """ 94 126 Returns True if the current transaction requires a commit for changes to 95 happen. 127 happen. Warns if no known changes happened, but a commit/rollback is 128 still required. 96 129 """ 97 130 if using is None: 98 131 using = DEFAULT_DB_ALIAS 99 return dirty.get(thread.get_ident(), {}).get(using, False) 132 flag = _is_dirty(deprecated_dirty, using) 133 if flag != _is_dirty(dirty, using): 134 warn("Read-only transactions will be considered dirty in the future", 135 ReadonlyTransactionWarning) 136 return flag 100 137 101 def set_dirty(using=None):138 def _set_dirty(dirty_dict, using=None): 102 139 """ 103 140 Sets a dirty flag for the current thread and code streak. This can be used 104 141 to decide in a managed block of code to decide whether there are open 105 142 changes waiting for commit. 106 143 """ 144 # TODO: Once deprecated_dirty is eliminated, fold this back into set_dirty 107 145 if using is None: 108 146 using = DEFAULT_DB_ALIAS 109 147 thread_ident = thread.get_ident() 110 if thread_ident in dirty and using in dirty[thread_ident]:111 dirty [thread_ident][using] = True148 if thread_ident in dirty_dict and using in dirty_dict[thread_ident]: 149 dirty_dict[thread_ident][using] = True 112 150 else: 113 151 raise TransactionManagementError("This code isn't under transaction management") 114 152 115 def set_ clean(using=None):153 def set_dirty(using=None): 116 154 """ 155 Sets a dirty flag for the current thread and code streak. This can be used 156 to decide in a managed block of code to decide whether there are open 157 changes waiting for commit. 158 """ 159 _set_dirty(deprecated_dirty, using) 160 _set_dirty(dirty, using) 161 162 def _set_clean(dirty_dict, using=None): 163 """ 117 164 Resets a dirty flag for the current thread and code streak. This can be used 118 165 to decide in a managed block of code to decide whether a commit or rollback 119 166 should happen. 120 167 """ 168 # TODO: Once deprecated_dirty is eliminated, fold this back into set_clean 121 169 if using is None: 122 170 using = DEFAULT_DB_ALIAS 123 171 thread_ident = thread.get_ident() 124 if thread_ident in dirty and using in dirty[thread_ident]:125 dirty [thread_ident][using] = False172 if thread_ident in dirty_dict and using in dirty_dict[thread_ident]: 173 dirty_dict[thread_ident][using] = False 126 174 else: 127 175 raise TransactionManagementError("This code isn't under transaction management") 176 177 def set_clean(using=None): 178 _set_clean(deprecated_dirty, using) 179 _set_clean(dirty, using) 128 180 clean_savepoints(using=using) 129 181 130 182 def clean_savepoints(using=None): … … 347 399 def exiting(exc_value, using): 348 400 try: 349 401 if exc_value is not None: 350 if is_dirty(using=using):402 if _is_dirty(dirty, using=using): 351 403 rollback(using=using) 352 404 else: 353 if is_dirty(using=using):405 if _is_dirty(dirty, using=using): 354 406 try: 355 407 commit(using=using) 356 408 except: -
django/middleware/transaction.py
1 1 from django.db import transaction 2 2 3 def _transaction_is_dirty(): 4 """ 5 While the sense of a dirty transaction is being changed, 6 this is the way to use the "new sense" (any touching of the 7 db makes the transaction dirty) 8 """ 9 # TODO: once transaction.deprecated_dirty is eliminated, delete this 10 # and use transaction.is_dirty instead 11 return transaction._is_dirty(transaction.dirty) 12 3 13 class TransactionMiddleware(object): 4 14 """ 5 15 Transaction middleware. If this is enabled, each view function will be run … … 14 24 15 25 def process_exception(self, request, exception): 16 26 """Rolls back the database and leaves transaction management""" 17 if transaction.is_dirty():27 if _transaction_is_dirty(): 18 28 transaction.rollback() 19 29 transaction.leave_transaction_management() 20 30 21 31 def process_response(self, request, response): 22 32 """Commits and leaves transaction management.""" 23 33 if transaction.is_managed(): 24 if transaction.is_dirty():34 if _transaction_is_dirty(): 25 35 transaction.commit() 26 36 transaction.leave_transaction_management() 27 37 return response 38 -
tests/regressiontests/transaction_regress/tests.py
1 from warnings import filterwarnings 2 from django.test import TransactionTestCase 3 from django.db import connection, transaction 4 from django.db.transaction import \ 5 commit_on_success, commit_manually, \ 6 TransactionManagementError, ReadonlyTransactionWarning 7 from models import Mod 8 9 class Test9964(TransactionTestCase): 10 11 def setUp(self): 12 filterwarnings("error", ".*", ReadonlyTransactionWarning) 13 14 def test_raw_committed_on_success(self): 15 16 @commit_on_success 17 def raw_sql(): 18 cursor = connection.cursor() 19 cursor.execute("INSERT into transaction_regress_mod (id,fld) values (17,18)") 20 21 raw_sql() 22 transaction.rollback() 23 try: 24 obj = Mod.objects.get(pk=17) 25 self.assertEqual(obj.fld, 18) 26 except Mod.DoesNotExist: 27 self.fail("transaction with raw sql not committed") 28 29 def test_commit_manually_enforced(self): 30 @commit_manually 31 def non_comitter(): 32 Mod.objects.create(fld=55) 33 34 self.assertRaises(TransactionManagementError, non_comitter) 35 36 def test_commit_manually_enforced_readonly(self): 37 @commit_manually 38 def non_comitter(): 39 _ = Mod.objects.count() 40 41 self.assertRaises(ReadonlyTransactionWarning, non_comitter) 42 43 def test_commit_manually_commit_ok(self): 44 @commit_manually 45 def committer(): 46 _ = Mod.objects.count() 47 transaction.commit() 48 49 try: 50 committer() 51 except TransactionManagementError: 52 self.fail("Commit did not clear the transaction state") 53 54 def test_commit_manually_rollback_ok(self): 55 @commit_manually 56 def roller_back(): 57 _ = Mod.objects.count() 58 transaction.rollback() 59 60 try: 61 roller_back() 62 except TransactionManagementError: 63 self.fail("Rollback did not clear the transaction state") 64 65 def test_commit_manually_enforced_after_commit(self): 66 @commit_manually 67 def fake_committer(): 68 _ = Mod.objects.count() 69 transaction.commit() 70 _ = Mod.objects.count() 71 72 self.assertRaises(ReadonlyTransactionWarning, fake_committer) 73 74 def test_reuse_cursor_reference(self): 75 @commit_on_success 76 def reuse_cursor_ref(): 77 cursor = connection.cursor() 78 cursor.execute("INSERT into transaction_regress_mod (id,fld) values (1,2)") 79 transaction.rollback() 80 cursor.execute("INSERT into transaction_regress_mod (id,fld) values (1,2)") 81 82 reuse_cursor_ref() 83 transaction.rollback() 84 try: 85 obj = Mod.objects.get(pk=1) 86 self.assertEquals(obj.fld, 2) 87 except Mod.DoesNotExist: 88 self.fail("After ending a transaction, cursor use no longer sets dirty") 89 90 def test_6669(self): 91 92 from django.contrib.auth.models import User 93 94 @transaction.commit_on_success 95 def create_system_user(): 96 user = User.objects.create_user(username='system', password='iamr00t', email='root@SITENAME.com') 97 Mod.objects.create(fld=user.id) 98 99 try: 100 create_system_user() 101 except: 102 pass 103 104 try: 105 create_system_user() 106 except: 107 pass 108 109 User.objects.all()[0] -
tests/regressiontests/transaction_regress/models.py
1 from django.db import models 2 3 class Mod(models.Model): 4 fld = models.IntegerField() -
tests/regressiontests/delete_regress/tests.py
61 61 Book.objects.filter(pagecount__lt=250).delete() 62 62 transaction.commit() 63 63 self.assertEqual(1, Book.objects.count()) 64 transaction.commit() 64 65 65 66 class DeleteCascadeTests(TestCase): 66 67 def test_generic_relation_cascade(self): -
tests/regressiontests/fixtures_regress/tests.py
610 610 self.assertEqual(Thingy.objects.count(), 1) 611 611 transaction.rollback() 612 612 self.assertEqual(Thingy.objects.count(), 0) 613 transaction.commit() 613 614 614 615 def test_ticket_11101(self): 615 616 """Test that fixtures can be rolled back (ticket #11101).""" -
docs/topics/db/sql.txt
231 231 232 232 Transactions and raw SQL 233 233 ------------------------ 234 If you are using transaction decorators (such as ``commit_on_success``) to235 wrap your views and provide transaction control, you don't have to make a236 manual call to ``transaction.commit_unless_managed()`` -- you can manually237 commit if you want to, but you aren't required to, since the decorator will238 commit for you. However, if you don't manually commit your changes, you will239 need to manually mark the transaction as dirty, using240 ``transaction.set_dirty()``::241 234 242 @commit_on_success 243 def my_custom_sql_view(request, value): 244 from django.db import connection, transaction 245 cursor = connection.cursor() 235 .. versionchanged:: 1.3 236 calling ``set_dirty()`` no longer required in managed transactions 246 237 247 # Data modifying operation 248 cursor.execute("UPDATE bar SET foo = 1 WHERE baz = %s", [value]) 238 If you write functions that modify the database via raw SQL, you need to 239 consider the transaction management mode they run under. If this may be 240 Django's default mode (by default or using the ``auto_commit`` 241 decorator), you need to make manual calls to 242 ``transaction.commit_unless_managed()`` to make sure the changes are 243 committed. If you are using managed transactions (e.g. with the 244 ``commit_on_success`` decorator), you don't have to make such calls. 249 245 250 # Since we modified data, mark the transaction as dirty251 transaction.set_dirty()252 246 253 # Data retrieval operation. This doesn't dirty the transaction,254 # so no call to set_dirty() is required.255 cursor.execute("SELECT foo FROM bar WHERE baz = %s", [value])256 row = cursor.fetchone()257 258 return render_to_response('template.html', {'row': row})259 260 The call to ``set_dirty()`` is made automatically when you use the Django ORM261 to make data modifying database calls. However, when you use raw SQL, Django262 has no way of knowing if your SQL modifies data or not. The manual call to263 ``set_dirty()`` ensures that Django knows that there are modifications that264 must be committed.265 266 247 Connections and cursors 267 248 ----------------------- 268 249 -
docs/releases/1.3.txt
328 328 >>> formset = ArticleFormSet() 329 329 >>> formset = ArticleFormSet(data=None) 330 330 331 332 333 331 .. _deprecated-features-1.3: 334 332 335 333 Features deprecated in 1.3 … … 475 473 Those commands have been deprecated. The ``flush`` and ``sqlflush`` commands 476 474 can be used to delete everything. You can also use ALTER TABLE or DROP TABLE 477 475 statements manually. 476 477 478 Changes in transaction management 479 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 480 481 When using managed transactions -- that is, anything but the default 482 autocommit mode -- it is important when a transaction is marked as 483 "dirty". Dirty transactions are committed by the ``commit_on_success`` 484 decorator or the ``TransactionMiddleware``, and ``commit_manually`` 485 forces them to be closed explicitly; clean transactions "get a pass", 486 which means they are usually rolled back at the end of a request 487 when the connection is closed. 488 489 Until Django 1.3, transactions were only marked dirty when Django 490 was aware of a modifying operation performed in them; that is, either 491 some model was saved, some bulk update or delete was performed, or 492 the user explicitly called ``transaction.set_dirty()``. Django 1.3 493 starts a transition to stricter semantics, where a transaction is 494 marked dirty when any database operation is performed; 495 this means you no longer need to set a transaction dirty explicitly 496 when you execute raw SQL or use a data-modifying ``select`` in a 497 managed transaction. 498 499 On the other hand, the new semantics requires you to explicitly close 500 read-only transactions under ``commit_manually``, and implies a different 501 return value for ``django.db.transaction.is_dirty()`` in some 502 circumstances. These cases (read-only transactions left pending, and 503 calls to ``is_dirty()`` where the new semantics is different) issue a 504 ``ReadonlyTransactionWarning`` in Django 1.3 and 1.4; in Django 1.5, 505 ``is_dirty()`` will return the new value, and transctions left pending 506 will raise a ``TransactionManagementError``.