Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

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
@aaugustin aaugustin authored
View
9 django/db/backends/postgresql_psycopg2/base.py
@@ -83,7 +83,8 @@ def __init__(self, *args, **kwargs):
if autocommit:
level = psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT
else:
- level = psycopg2.extensions.ISOLATION_LEVEL_READ_COMMITTED
+ level = self.settings_dict["OPTIONS"].get('isolation_level',
+ psycopg2.extensions.ISOLATION_LEVEL_READ_COMMITTED)
self._set_isolation_level(level)
self.ops = DatabaseOperations(self)
self.client = DatabaseClient(self)
@@ -104,6 +105,8 @@ def get_connection_params(self):
conn_params.update(settings_dict['OPTIONS'])
if 'autocommit' in conn_params:
del conn_params['autocommit']
+ if 'isolation_level' in conn_params:
+ del conn_params['isolation_level']
if settings_dict['USER']:
conn_params['user'] = settings_dict['USER']
if settings_dict['PASSWORD']:
@@ -170,7 +173,9 @@ def _enter_transaction_management(self, managed):
the same transaction is visible across all the queries.
"""
if self.features.uses_autocommit and managed and not self.isolation_level:
- self._set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_READ_COMMITTED)
+ level = self.settings_dict["OPTIONS"].get('isolation_level',
+ psycopg2.extensions.ISOLATION_LEVEL_READ_COMMITTED)
+ self._set_isolation_level(level)
def _leave_transaction_management(self, managed):
"""
View
35 docs/ref/databases.txt
@@ -143,8 +143,11 @@ autocommit behavior is enabled by setting the ``autocommit`` key in
the :setting:`OPTIONS` part of your database configuration in
:setting:`DATABASES`::
- 'OPTIONS': {
- 'autocommit': True,
+ DATABASES = {
+ # ...
+ 'OPTIONS': {
+ 'autocommit': True,
+ },
}
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
before enabling this feature. It's faster, but it provides less automatic
protection for multi-call operations.
+Isolation level
+~~~~~~~~~~~~~~~
+
+.. versionadded:: 1.6
+
+Like PostgreSQL itself, Django defaults to the ``READ COMMITTED`` `isolation
+level <postgresql-isolation-levels>`_. If you need a higher isolation level
+such as ``REPEATABLE READ`` or ``SERIALIZABLE``, set it in the
+:setting:`OPTIONS` part of your database configuration in
+:setting:`DATABASES`::
+
+ import psycopg2.extensions
+
+ DATABASES = {
+ # ...
+ 'OPTIONS': {
+ 'isolation_level': psycopg2.extensions.ISOLATION_LEVEL_SERIALIZABLE,
+ },
+ }
+
+.. note::
+
+ Under higher isolation levels, your application should be prepared to
+ handle exceptions raised on serialization failures. This option is
+ designed for advanced uses.
+
+.. _postgresql-isolation-levels: http://www.postgresql.org/docs/devel/static/transaction-iso.html
+
Indexes for ``varchar`` and ``text`` columns
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
View
2  docs/releases/1.6.txt
@@ -125,6 +125,8 @@ Minor features
* The admin list columns have a ``column-<field_name>`` class in the HTML
so the columns header can be styled with CSS, e.g. to set a column width.
+* The isolation level can be customized under PostgreSQL.
+
Backwards incompatible changes in 1.6
=====================================
View
24 tests/transactions_regress/tests.py
@@ -242,17 +242,18 @@ def test_commit_unless_managed_in_managed(self):
@skipUnless(connection.vendor == 'postgresql',
"This test only valid for PostgreSQL")
-class TestPostgresAutocommit(TransactionTestCase):
+class TestPostgresAutocommitAndIsolation(TransactionTestCase):
"""
- Tests to make sure psycopg2's autocommit mode is restored after entering
- and leaving transaction management. Refs #16047.
+ Tests to make sure psycopg2's autocommit mode and isolation level
+ is restored after entering and leaving transaction management.
+ Refs #16047, #18130.
"""
def setUp(self):
from psycopg2.extensions import (ISOLATION_LEVEL_AUTOCOMMIT,
- ISOLATION_LEVEL_READ_COMMITTED,
+ ISOLATION_LEVEL_SERIALIZABLE,
TRANSACTION_STATUS_IDLE)
self._autocommit = ISOLATION_LEVEL_AUTOCOMMIT
- self._read_committed = ISOLATION_LEVEL_READ_COMMITTED
+ self._serializable = ISOLATION_LEVEL_SERIALIZABLE
self._idle = TRANSACTION_STATUS_IDLE
# We want a clean backend with autocommit = True, so
@@ -261,6 +262,7 @@ def setUp(self):
settings = self._old_backend.settings_dict.copy()
opts = settings['OPTIONS'].copy()
opts['autocommit'] = True
+ opts['isolation_level'] = ISOLATION_LEVEL_SERIALIZABLE
settings['OPTIONS'] = opts
new_backend = self._old_backend.__class__(settings, DEFAULT_DB_ALIAS)
connections[DEFAULT_DB_ALIAS] = new_backend
@@ -279,7 +281,7 @@ def test_initial_autocommit_state(self):
def test_transaction_management(self):
transaction.enter_transaction_management()
transaction.managed(True)
- self.assertEqual(connection.isolation_level, self._read_committed)
+ self.assertEqual(connection.isolation_level, self._serializable)
transaction.leave_transaction_management()
self.assertEqual(connection.isolation_level, self._autocommit)
@@ -287,13 +289,13 @@ def test_transaction_management(self):
def test_transaction_stacking(self):
transaction.enter_transaction_management()
transaction.managed(True)
- self.assertEqual(connection.isolation_level, self._read_committed)
+ self.assertEqual(connection.isolation_level, self._serializable)
transaction.enter_transaction_management()
- self.assertEqual(connection.isolation_level, self._read_committed)
+ self.assertEqual(connection.isolation_level, self._serializable)
transaction.leave_transaction_management()
- self.assertEqual(connection.isolation_level, self._read_committed)
+ self.assertEqual(connection.isolation_level, self._serializable)
transaction.leave_transaction_management()
self.assertEqual(connection.isolation_level, self._autocommit)
@@ -301,7 +303,7 @@ def test_transaction_stacking(self):
def test_enter_autocommit(self):
transaction.enter_transaction_management()
transaction.managed(True)
- self.assertEqual(connection.isolation_level, self._read_committed)
+ self.assertEqual(connection.isolation_level, self._serializable)
list(Mod.objects.all())
self.assertTrue(transaction.is_dirty())
# Enter autocommit mode again.
@@ -314,7 +316,7 @@ def test_enter_autocommit(self):
list(Mod.objects.all())
self.assertFalse(transaction.is_dirty())
transaction.leave_transaction_management()
- self.assertEqual(connection.isolation_level, self._read_committed)
+ self.assertEqual(connection.isolation_level, self._serializable)
transaction.leave_transaction_management()
self.assertEqual(connection.isolation_level, self._autocommit)
Please sign in to comment.
Something went wrong with that request. Please try again.