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
Back to Top