Ticket #9964: 9964-make-managed-transactions-dirty-v2-full.patch
File 9964-make-managed-transactions-dirty-v2-full.patch, 11.2 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.alias = alias 23 23 self.vendor = 'unknown' 24 24 self.use_debug_cursor = None 25 self.on_used = None 25 26 26 27 def __eq__(self, other): 27 28 return self.settings_dict == other.settings_dict … … 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
67 67 if thread_ident not in dirty or using not in dirty[thread_ident]: 68 68 dirty.setdefault(thread_ident, {}) 69 69 dirty[thread_ident][using] = False 70 def set_dirty_if_managed(): 71 if is_managed(using): set_dirty(using) 72 connection.on_used = set_dirty_if_managed 70 73 connection._enter_transaction_management(managed) 71 74 72 75 def leave_transaction_management(using=None): -
tests/regressiontests/transaction_regress/tests.py
1 from django.test import TransactionTestCase 2 from django.db import connection, transaction 3 from django.db.transaction import \ 4 commit_on_success, commit_manually, \ 5 TransactionManagementError 6 from models import Mod 7 8 class Test9964(TransactionTestCase): 9 10 def test_raw_committed_on_success(self): 11 12 @commit_on_success 13 def raw_sql(): 14 cursor = connection.cursor() 15 cursor.execute("INSERT into transaction_regress_mod (id,fld) values (17,18)") 16 17 raw_sql() 18 transaction.rollback() 19 try: 20 obj = Mod.objects.get(pk=17) 21 self.assertEqual(obj.fld, 18) 22 except Mod.DoesNotExist: 23 self.fail("transaction with raw sql not committed") 24 25 def test_commit_manually_enforced(self): 26 @commit_manually 27 def non_comitter(): 28 _ = Mod.objects.count() 29 30 self.assertRaises(TransactionManagementError, non_comitter) 31 32 def test_commit_manually_commit_ok(self): 33 @commit_manually 34 def committer(): 35 _ = Mod.objects.count() 36 transaction.commit() 37 38 try: 39 committer() 40 except TransactionManagementError: 41 self.fail("Commit did not clear the transaction state") 42 43 def test_commit_manually_rollback_ok(self): 44 @commit_manually 45 def roller_back(): 46 _ = Mod.objects.count() 47 transaction.rollback() 48 49 try: 50 roller_back() 51 except TransactionManagementError: 52 self.fail("Rollback did not clear the transaction state") 53 54 def test_commit_manually_enforced_after_commit(self): 55 @commit_manually 56 def fake_committer(): 57 _ = Mod.objects.count() 58 transaction.commit() 59 _ = Mod.objects.count() 60 61 self.assertRaises(TransactionManagementError, fake_committer) 62 63 def test_reuse_cursor_reference(self): 64 @commit_on_success 65 def reuse_cursor_ref(): 66 cursor = connection.cursor() 67 cursor.execute("INSERT into transaction_regress_mod (id,fld) values (1,2)") 68 transaction.rollback() 69 cursor.execute("INSERT into transaction_regress_mod (id,fld) values (1,2)") 70 71 reuse_cursor_ref() 72 transaction.rollback() 73 try: 74 obj = Mod.objects.get(pk=1) 75 self.assertEquals(obj.fld, 2) 76 except Mod.DoesNotExist: 77 self.fail("After ending a transaction, cursor use no longer sets dirty") 78 79 def test_6669(self): 80 81 from django.contrib.auth.models import User 82 83 @transaction.commit_on_success 84 def create_system_user(): 85 user = User.objects.create_user(username='system', password='iamr00t', email='root@SITENAME.com') 86 Mod.objects.create(fld=user.id) 87 88 try: 89 create_system_user() 90 except: 91 pass 92 93 try: 94 create_system_user() 95 except: 96 pass 97 98 print User.objects.all() -
tests/regressiontests/transaction_regress/models.py
1 from django.db import models 2 3 class Mod(models.Model): 4 fld = models.IntegerField() 5 from django.db import models 6 7 class Mod(models.Model): 8 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 246 236 247 # Data modifying operation 248 cursor.execute("UPDATE bar SET foo = 1 WHERE baz = %s", [value]) 237 If you write functions that modify the database via raw SQL, you need to 238 consider the transaction management mode they run under. If this may be 239 Django's default mode (by default or using the ``auto_commit`` 240 decorator), you need to make manual calls to 241 ``transaction.commit_unless_managed()`` to make sure the changes are 242 committed. If you are using transaction decorators (such as 243 ``commit_on_success``) to wrap your views and provide transaction 244 control, 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
264 264 official designation "Aceh (ACE)". 265 265 266 266 267 Transaction management 268 ~~~~~~~~~~~~~~~~~~~~~~ 269 270 When using managed transactions -- that is, anything but the default 271 autocommit mode -- it is important when a transaction is marked as 272 "dirty". Dirty transactions are committed by the ``commit_on_success`` 273 decorator or the ``TransactionMiddleware``, and ``commit_manually`` 274 forces them to be closed explicitly; clean transactions "get a pass", 275 which means they are usually rolled back at the end of a request 276 when the connection is closed. 277 278 Until Django 1.3, transactions were only marked dirty when Django 279 was aware of a modifying operation performed in them; that is, either 280 some model was saved, some bulk update or delete was performed, or 281 the user explicitly called ``transaction.set_dirty()``. In Django 1.3, 282 a transaction is marked dirty when any database operation is performed; 283 this means you no longer need to set a transaction dirty explicitly 284 when you execute raw SQL or use a data-modifying ``select``. On the 285 other hand, you do need to explicitly close read-only transactions 286 under ``commit_manually``. For example, take a look at this code:: 287 288 @transaction.commit_manually 289 def my_view(request, name): 290 obj = get_object_or_404(MyObject, name__iexact=name) 291 return render_to_response('template', {'object':obj}) 292 293 Until Django 1.3, this works fine. With Django 1.3, this raises a 294 ``TransactionManagementError``; you need to end the transaction 295 explicitly, one way or another. 296 267 297 .. _deprecated-features-1.3: 268 298 269 299 Features deprecated in 1.3