diff --git a/django/db/backends/postgresql_psycopg2/base.py b/django/db/backends/postgresql_psycopg2/base.py index 5f7ca69611..d110b120d7 100644 --- a/django/db/backends/postgresql_psycopg2/base.py +++ b/django/db/backends/postgresql_psycopg2/base.py @@ -5,6 +5,7 @@ Requires psycopg 2: http://initd.org/projects/psycopg2 """ from django.conf import settings +from django.core.exceptions import ImproperlyConfigured from django.db.backends.base.base import BaseDatabaseWrapper from django.db.backends.base.validation import BaseDatabaseValidation from django.utils.encoding import force_str @@ -16,9 +17,19 @@ try: import psycopg2.extensions import psycopg2.extras except ImportError as e: - from django.core.exceptions import ImproperlyConfigured raise ImproperlyConfigured("Error loading psycopg2 module: %s" % e) + +def psycopg2_version(): + version = psycopg2.__version__.split(' ', 1)[0] + return tuple(int(v) for v in version.split('.') if v.isdigit()) + +PSYCOPG2_VERSION = psycopg2_version() + +if PSYCOPG2_VERSION < (2, 4, 5): + raise ImproperlyConfigured("psycopg2_version 2.4.5 or newer is required; you have %s" % psycopg2.__version__) + + # Some of these import psycopg2, so import them after checking if it's installed. from .client import DatabaseClient # isort:skip from .creation import DatabaseCreation # isort:skip @@ -164,20 +175,15 @@ class DatabaseWrapper(BaseDatabaseWrapper): # - after connecting to the database in order to obtain the database's # default when no value is explicitly specified in options. # - before calling _set_autocommit() because if autocommit is on, that - # will set connection.isolation_level to ISOLATION_LEVEL_AUTOCOMMIT; - # and if autocommit is off, on psycopg2 < 2.4.2, _set_autocommit() - # needs self.isolation_level. + # will set connection.isolation_level to ISOLATION_LEVEL_AUTOCOMMIT. options = self.settings_dict['OPTIONS'] try: self.isolation_level = options['isolation_level'] except KeyError: self.isolation_level = connection.isolation_level else: - # Set the isolation level to the value from OPTIONS. This isn't - # needed on psycopg2 < 2.4.2 because it happens as a side-effect - # of _set_autocommit(False). - if (self.isolation_level != connection.isolation_level and - self.psycopg2_version >= (2, 4, 2)): + # Set the isolation level to the value from OPTIONS. + if self.isolation_level != connection.isolation_level: connection.set_session(isolation_level=self.isolation_level) return connection @@ -186,13 +192,7 @@ class DatabaseWrapper(BaseDatabaseWrapper): self.connection.set_client_encoding('UTF8') tz = self.settings_dict['TIME_ZONE'] - try: - get_parameter_status = self.connection.get_parameter_status - except AttributeError: - # psycopg2 < 2.0.12 doesn't have get_parameter_status - conn_tz = None - else: - conn_tz = get_parameter_status('TimeZone') + conn_tz = self.connection.get_parameter_status('TimeZone') if conn_tz != tz: cursor = self.connection.cursor() @@ -211,14 +211,7 @@ class DatabaseWrapper(BaseDatabaseWrapper): def _set_autocommit(self, autocommit): with self.wrap_database_errors: - if self.psycopg2_version >= (2, 4, 2): - self.connection.autocommit = autocommit - else: - if autocommit: - level = psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT - else: - level = self.isolation_level - self.connection.set_isolation_level(level) + self.connection.autocommit = autocommit def check_constraints(self, table_names=None): """ @@ -239,8 +232,7 @@ class DatabaseWrapper(BaseDatabaseWrapper): @cached_property def psycopg2_version(self): - version = psycopg2.__version__.split(' ', 1)[0] - return tuple(int(v) for v in version.split('.') if v.isdigit()) + return PSYCOPG2_VERSION @cached_property def pg_version(self): diff --git a/docs/ref/contrib/postgres/index.txt b/docs/ref/contrib/postgres/index.txt index 307b9befd5..c97a61d123 100644 --- a/docs/ref/contrib/postgres/index.txt +++ b/docs/ref/contrib/postgres/index.txt @@ -1,12 +1,17 @@ ``django.contrib.postgres`` =========================== +.. module:: django.contrib.postgres + :synopsis: PostgreSQL-specific fields and features + .. versionadded:: 1.8 PostgreSQL has a number of features which are not shared by the other databases Django supports. This optional module contains model fields and form fields for a number of PostgreSQL specific data types. +Psycopg2 2.5 or higher is required. + .. note:: Django is, and will continue to be, a database-agnostic web framework. We would encourage those writing reusable applications for the Django diff --git a/docs/ref/databases.txt b/docs/ref/databases.txt index ae4c588788..5fda5589e1 100644 --- a/docs/ref/databases.txt +++ b/docs/ref/databases.txt @@ -92,8 +92,10 @@ below for information on how to set up your database correctly. PostgreSQL notes ================ -Django supports PostgreSQL 9.0 and higher. It requires the use of Psycopg2 -2.0.9 or higher. +Django supports PostgreSQL 9.0 and higher. It requires the use of `psycopg2`_ +2.4.5 or higher (or 2.5+ if you want to use :mod:`django.contrib.postgres`). + +.. _psycopg2: http://initd.org/psycopg/ If you're on Windows, check out the unofficial `compiled Windows version`_ of psycopg2. diff --git a/docs/releases/1.8.txt b/docs/releases/1.8.txt index 720d590317..0200e18d48 100644 --- a/docs/releases/1.8.txt +++ b/docs/releases/1.8.txt @@ -834,7 +834,8 @@ officially supports. This also includes dropping support for PostGIS 1.3 and 1.4 as these versions are not supported on versions of PostgreSQL later than 8.4. -Django also now requires the use of Psycopg2 version 2.0.9 or higher. +Django also now requires the use of Psycopg2 version 2.4.5 or higher (or 2.5+ +if you want to use :mod:`django.contrib.postgres`). Support for MySQL versions older than 5.5 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/tests/backends/tests.py b/tests/backends/tests.py index 52c6d44051..fcd165146e 100644 --- a/tests/backends/tests.py +++ b/tests/backends/tests.py @@ -289,14 +289,14 @@ class PostgreSQLTests(TestCase): self.assertIn('::text', do.lookup_cast(lookup)) def test_correct_extraction_psycopg2_version(self): - from django.db.backends.postgresql_psycopg2.base import DatabaseWrapper + from django.db.backends.postgresql_psycopg2.base import psycopg2_version version_path = 'django.db.backends.postgresql_psycopg2.base.Database.__version__' with mock.patch(version_path, '2.6.9'): - self.assertEqual(DatabaseWrapper.psycopg2_version.__get__(self), (2, 6, 9)) + self.assertEqual(psycopg2_version(), (2, 6, 9)) with mock.patch(version_path, '2.5.dev0'): - self.assertEqual(DatabaseWrapper.psycopg2_version.__get__(self), (2, 5)) + self.assertEqual(psycopg2_version(), (2, 5)) class DateQuotingTest(TestCase): diff --git a/tests/requirements/postgres.txt b/tests/requirements/postgres.txt index 658130bb2c..4ff5f170a9 100644 --- a/tests/requirements/postgres.txt +++ b/tests/requirements/postgres.txt @@ -1 +1 @@ -psycopg2 +psycopg2>=2.5