Code

Ticket #9964: 9964-make-managed-transactions-dirty.patch

File 9964-make-managed-transactions-dirty.patch, 6.9 KB (added by shai, 4 years ago)

New patch for marking transactions dirty, against 14579

  • django/db/backends/__init__.py

     
    2222        self.alias = alias 
    2323        self.vendor = 'unknown' 
    2424        self.use_debug_cursor = None 
     25        self.on_used = None 
    2526 
    2627    def __eq__(self, other): 
    2728        return self.settings_dict == other.settings_dict 
     
    7879        if (self.use_debug_cursor or 
    7980            (self.use_debug_cursor is None and settings.DEBUG)): 
    8081            return self.make_debug_cursor(cursor) 
     82        # Used by transaction mechanism to be notified that db was used 
     83        if self.on_used: 
     84            self.on_used() 
    8185        return cursor 
    8286 
    8387    def make_debug_cursor(self, cursor): 
  • django/db/transaction.py

     
    6767    if thread_ident not in dirty or using not in dirty[thread_ident]: 
    6868        dirty.setdefault(thread_ident, {}) 
    6969        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 
    7073    connection._enter_transaction_management(managed) 
    7174 
    7275def leave_transaction_management(using=None): 
  • tests/regressiontests/transaction_regress/tests.py

     
     1from django.test import TransactionTestCase 
     2from django.db import connection, transaction 
     3from django.db.transaction import \ 
     4    commit_on_success, commit_manually, \ 
     5    TransactionManagementError 
     6from models import Mod 
     7 
     8class 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 
  • tests/regressiontests/transaction_regress/models.py

     
     1from django.db import models 
     2 
     3class Mod(models.Model): 
     4    fld = models.IntegerField() 
  • tests/regressiontests/fixtures_regress/tests.py

     
    613613 
    614614    def test_ticket_11101(self): 
    615615        """Test that fixtures can be rolled back (ticket #11101).""" 
    616         ticket_11101 = transaction.commit_manually(self.ticket_11101) 
     616        ticket_11101 = transaction.commit_on_success(self.ticket_11101) 
    617617        ticket_11101() 
  • docs/topics/db/sql.txt

     
    231231 
    232232Transactions and raw SQL 
    233233------------------------ 
    234 If you are using transaction decorators (such as ``commit_on_success``) to 
    235 wrap your views and provide transaction control, you don't have to make a 
    236 manual call to ``transaction.commit_unless_managed()`` -- you can manually 
    237 commit if you want to, but you aren't required to, since the decorator will 
    238 commit for you. However, if you don't manually commit your changes, you will 
    239 need to manually mark the transaction as dirty, using 
    240 ``transaction.set_dirty()``:: 
    241234 
    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 
    246236 
    247         # Data modifying operation 
    248         cursor.execute("UPDATE bar SET foo = 1 WHERE baz = %s", [value]) 
     237If you write functions that modify the database via raw SQL, you need to 
     238consider the transaction management mode they run under. If this may be 
     239Django's default mode (by default or using the ``auto_commit`` 
     240decorator), you need to make manual calls to 
     241``transaction.commit_unless_managed()`` to make sure the changes are 
     242committed. If you are using transaction decorators (such as 
     243``commit_on_success``) to wrap your views and provide transaction 
     244control, you don't have to make such calls. 
    249245 
    250         # Since we modified data, mark the transaction as dirty 
    251         transaction.set_dirty() 
    252246 
    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 ORM 
    261 to make data modifying database calls. However, when you use raw SQL, Django 
    262 has no way of knowing if your SQL modifies data or not. The manual call to 
    263 ``set_dirty()`` ensures that Django knows that there are modifications that 
    264 must be committed. 
    265  
    266247Connections and cursors 
    267248----------------------- 
    268249