Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Fixed #18130 -- Made the isolation level configurable on PostgreSQL.

Thanks limscoder for the report and niwi for the draft patch.
  • Loading branch information...
commit e0449316ebacaa550e9c529f8c9cb9a9b44e3765 1 parent d63e550
Aymeric Augustin authored March 02, 2013
9  django/db/backends/postgresql_psycopg2/base.py
@@ -83,7 +83,8 @@ def __init__(self, *args, **kwargs):
83 83
         if autocommit:
84 84
             level = psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT
85 85
         else:
86  
-            level = psycopg2.extensions.ISOLATION_LEVEL_READ_COMMITTED
  86
+            level = self.settings_dict["OPTIONS"].get('isolation_level',
  87
+                psycopg2.extensions.ISOLATION_LEVEL_READ_COMMITTED)
87 88
         self._set_isolation_level(level)
88 89
         self.ops = DatabaseOperations(self)
89 90
         self.client = DatabaseClient(self)
@@ -104,6 +105,8 @@ def get_connection_params(self):
104 105
         conn_params.update(settings_dict['OPTIONS'])
105 106
         if 'autocommit' in conn_params:
106 107
             del conn_params['autocommit']
  108
+        if 'isolation_level' in conn_params:
  109
+            del conn_params['isolation_level']
107 110
         if settings_dict['USER']:
108 111
             conn_params['user'] = settings_dict['USER']
109 112
         if settings_dict['PASSWORD']:
@@ -170,7 +173,9 @@ def _enter_transaction_management(self, managed):
170 173
         the same transaction is visible across all the queries.
171 174
         """
172 175
         if self.features.uses_autocommit and managed and not self.isolation_level:
173  
-            self._set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_READ_COMMITTED)
  176
+            level = self.settings_dict["OPTIONS"].get('isolation_level',
  177
+                psycopg2.extensions.ISOLATION_LEVEL_READ_COMMITTED)
  178
+            self._set_isolation_level(level)
174 179
 
175 180
     def _leave_transaction_management(self, managed):
176 181
         """
35  docs/ref/databases.txt
@@ -143,8 +143,11 @@ autocommit behavior is enabled by setting the ``autocommit`` key in
143 143
 the :setting:`OPTIONS` part of your database configuration in
144 144
 :setting:`DATABASES`::
145 145
 
146  
-    'OPTIONS': {
147  
-        'autocommit': True,
  146
+    DATABASES = {
  147
+        # ...
  148
+        'OPTIONS': {
  149
+            'autocommit': True,
  150
+        },
148 151
     }
149 152
 
150 153
 In this configuration, Django still ensures that :ref:`delete()
@@ -168,6 +171,34 @@ You should also audit your existing code for any instances of this behavior
168 171
 before enabling this feature. It's faster, but it provides less automatic
169 172
 protection for multi-call operations.
170 173
 
  174
+Isolation level
  175
+~~~~~~~~~~~~~~~
  176
+
  177
+.. versionadded:: 1.6
  178
+
  179
+Like PostgreSQL itself, Django defaults to the ``READ COMMITTED`` `isolation
  180
+level <postgresql-isolation-levels>`_. If you need a higher isolation level
  181
+such as ``REPEATABLE READ`` or ``SERIALIZABLE``, set it in the
  182
+:setting:`OPTIONS` part of your database configuration in
  183
+:setting:`DATABASES`::
  184
+
  185
+    import psycopg2.extensions
  186
+
  187
+    DATABASES = {
  188
+        # ...
  189
+        'OPTIONS': {
  190
+            'isolation_level': psycopg2.extensions.ISOLATION_LEVEL_SERIALIZABLE,
  191
+        },
  192
+    }
  193
+
  194
+.. note::
  195
+
  196
+    Under higher isolation levels, your application should be prepared to
  197
+    handle exceptions raised on serialization failures. This option is
  198
+    designed for advanced uses.
  199
+
  200
+.. _postgresql-isolation-levels: http://www.postgresql.org/docs/devel/static/transaction-iso.html
  201
+
171 202
 Indexes for ``varchar`` and ``text`` columns
172 203
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
173 204
 
2  docs/releases/1.6.txt
@@ -125,6 +125,8 @@ Minor features
125 125
 * The admin list columns have a ``column-<field_name>`` class in the HTML
126 126
   so the columns header can be styled with CSS, e.g. to set a column width.
127 127
 
  128
+* The isolation level can be customized under PostgreSQL.
  129
+
128 130
 Backwards incompatible changes in 1.6
129 131
 =====================================
130 132
 
24  tests/transactions_regress/tests.py
@@ -242,17 +242,18 @@ def test_commit_unless_managed_in_managed(self):
242 242
 
243 243
 @skipUnless(connection.vendor == 'postgresql',
244 244
             "This test only valid for PostgreSQL")
245  
-class TestPostgresAutocommit(TransactionTestCase):
  245
+class TestPostgresAutocommitAndIsolation(TransactionTestCase):
246 246
     """
247  
-    Tests to make sure psycopg2's autocommit mode is restored after entering
248  
-    and leaving transaction management. Refs #16047.
  247
+    Tests to make sure psycopg2's autocommit mode and isolation level
  248
+    is restored after entering and leaving transaction management.
  249
+    Refs #16047, #18130.
249 250
     """
250 251
     def setUp(self):
251 252
         from psycopg2.extensions import (ISOLATION_LEVEL_AUTOCOMMIT,
252  
-                                         ISOLATION_LEVEL_READ_COMMITTED,
  253
+                                         ISOLATION_LEVEL_SERIALIZABLE,
253 254
                                          TRANSACTION_STATUS_IDLE)
254 255
         self._autocommit = ISOLATION_LEVEL_AUTOCOMMIT
255  
-        self._read_committed = ISOLATION_LEVEL_READ_COMMITTED
  256
+        self._serializable = ISOLATION_LEVEL_SERIALIZABLE
256 257
         self._idle = TRANSACTION_STATUS_IDLE
257 258
 
258 259
         # We want a clean backend with autocommit = True, so
@@ -261,6 +262,7 @@ def setUp(self):
261 262
         settings = self._old_backend.settings_dict.copy()
262 263
         opts = settings['OPTIONS'].copy()
263 264
         opts['autocommit'] = True
  265
+        opts['isolation_level'] = ISOLATION_LEVEL_SERIALIZABLE
264 266
         settings['OPTIONS'] = opts
265 267
         new_backend = self._old_backend.__class__(settings, DEFAULT_DB_ALIAS)
266 268
         connections[DEFAULT_DB_ALIAS] = new_backend
@@ -279,7 +281,7 @@ def test_initial_autocommit_state(self):
279 281
     def test_transaction_management(self):
280 282
         transaction.enter_transaction_management()
281 283
         transaction.managed(True)
282  
-        self.assertEqual(connection.isolation_level, self._read_committed)
  284
+        self.assertEqual(connection.isolation_level, self._serializable)
283 285
 
284 286
         transaction.leave_transaction_management()
285 287
         self.assertEqual(connection.isolation_level, self._autocommit)
@@ -287,13 +289,13 @@ def test_transaction_management(self):
287 289
     def test_transaction_stacking(self):
288 290
         transaction.enter_transaction_management()
289 291
         transaction.managed(True)
290  
-        self.assertEqual(connection.isolation_level, self._read_committed)
  292
+        self.assertEqual(connection.isolation_level, self._serializable)
291 293
 
292 294
         transaction.enter_transaction_management()
293  
-        self.assertEqual(connection.isolation_level, self._read_committed)
  295
+        self.assertEqual(connection.isolation_level, self._serializable)
294 296
 
295 297
         transaction.leave_transaction_management()
296  
-        self.assertEqual(connection.isolation_level, self._read_committed)
  298
+        self.assertEqual(connection.isolation_level, self._serializable)
297 299
 
298 300
         transaction.leave_transaction_management()
299 301
         self.assertEqual(connection.isolation_level, self._autocommit)
@@ -301,7 +303,7 @@ def test_transaction_stacking(self):
301 303
     def test_enter_autocommit(self):
302 304
         transaction.enter_transaction_management()
303 305
         transaction.managed(True)
304  
-        self.assertEqual(connection.isolation_level, self._read_committed)
  306
+        self.assertEqual(connection.isolation_level, self._serializable)
305 307
         list(Mod.objects.all())
306 308
         self.assertTrue(transaction.is_dirty())
307 309
         # Enter autocommit mode again.
@@ -314,7 +316,7 @@ def test_enter_autocommit(self):
314 316
         list(Mod.objects.all())
315 317
         self.assertFalse(transaction.is_dirty())
316 318
         transaction.leave_transaction_management()
317  
-        self.assertEqual(connection.isolation_level, self._read_committed)
  319
+        self.assertEqual(connection.isolation_level, self._serializable)
318 320
         transaction.leave_transaction_management()
319 321
         self.assertEqual(connection.isolation_level, self._autocommit)
320 322
 

0 notes on commit e044931

Please sign in to comment.
Something went wrong with that request. Please try again.