diff --git a/django/contrib/postgres/apps.py b/django/contrib/postgres/apps.py index 494cea245a..a8ee3fbf4b 100644 --- a/django/contrib/postgres/apps.py +++ b/django/contrib/postgres/apps.py @@ -1,8 +1,7 @@ -from psycopg2.extras import DateRange, DateTimeRange, DateTimeTZRange, NumericRange - from django.apps import AppConfig from django.core.signals import setting_changed from django.db import connections +from django.db.backends.postgresql.psycopg_any import RANGE_TYPES from django.db.backends.signals import connection_created from django.db.migrations.writer import MigrationWriter from django.db.models import CharField, OrderBy, TextField @@ -21,8 +20,6 @@ from .lookups import ( from .serializers import RangeSerializer from .signals import register_type_handlers -RANGE_TYPES = (DateRange, DateTimeRange, DateTimeTZRange, NumericRange) - def uninstall_if_needed(setting, value, enter, **kwargs): """ diff --git a/django/contrib/postgres/fields/ranges.py b/django/contrib/postgres/fields/ranges.py index 6230b2f887..d5c438dbdc 100644 --- a/django/contrib/postgres/fields/ranges.py +++ b/django/contrib/postgres/fields/ranges.py @@ -1,10 +1,14 @@ import datetime import json -from psycopg2.extras import DateRange, DateTimeTZRange, NumericRange, Range - from django.contrib.postgres import forms, lookups from django.db import models +from django.db.backends.postgresql.psycopg_any import ( + DateRange, + DateTimeTZRange, + NumericRange, + Range, +) from django.db.models.lookups import PostgresOperatorLookup from .utils import AttributeSetter diff --git a/django/contrib/postgres/forms/ranges.py b/django/contrib/postgres/forms/ranges.py index 444991970d..3017f08e88 100644 --- a/django/contrib/postgres/forms/ranges.py +++ b/django/contrib/postgres/forms/ranges.py @@ -1,7 +1,10 @@ -from psycopg2.extras import DateRange, DateTimeTZRange, NumericRange - from django import forms from django.core import exceptions +from django.db.backends.postgresql.psycopg_any import ( + DateRange, + DateTimeTZRange, + NumericRange, +) from django.forms.widgets import HiddenInput, MultiWidget from django.utils.translation import gettext_lazy as _ diff --git a/django/db/backends/postgresql/creation.py b/django/db/backends/postgresql/creation.py index 690bf9639b..b445b89646 100644 --- a/django/db/backends/postgresql/creation.py +++ b/django/db/backends/postgresql/creation.py @@ -1,9 +1,8 @@ import sys -from psycopg2 import errors - from django.core.exceptions import ImproperlyConfigured from django.db.backends.base.creation import BaseDatabaseCreation +from django.db.backends.postgresql.psycopg_any import errors from django.db.backends.utils import strip_quotes diff --git a/django/db/backends/postgresql/operations.py b/django/db/backends/postgresql/operations.py index 62273fc43c..e86628ede2 100644 --- a/django/db/backends/postgresql/operations.py +++ b/django/db/backends/postgresql/operations.py @@ -1,11 +1,9 @@ import json from functools import lru_cache, partial -from psycopg2.extras import Inet -from psycopg2.extras import Json as Jsonb - from django.conf import settings from django.db.backends.base.operations import BaseDatabaseOperations +from django.db.backends.postgresql.psycopg_any import Inet, Jsonb from django.db.backends.utils import split_tzname_delta from django.db.models.constants import OnConflict diff --git a/django/db/backends/postgresql/psycopg_any.py b/django/db/backends/postgresql/psycopg_any.py new file mode 100644 index 0000000000..6dcd68f5eb --- /dev/null +++ b/django/db/backends/postgresql/psycopg_any.py @@ -0,0 +1,6 @@ +from psycopg2 import errors, extensions # NOQA +from psycopg2.extras import DateRange, DateTimeRange, DateTimeTZRange, Inet # NOQA +from psycopg2.extras import Json as Jsonb # NOQA +from psycopg2.extras import NumericRange, Range # NOQA + +RANGE_TYPES = (DateRange, DateTimeRange, DateTimeTZRange, NumericRange) diff --git a/django/db/backends/postgresql/schema.py b/django/db/backends/postgresql/schema.py index 68c5bf31af..0c2043db35 100644 --- a/django/db/backends/postgresql/schema.py +++ b/django/db/backends/postgresql/schema.py @@ -1,7 +1,6 @@ -import psycopg2 - from django.db.backends.base.schema import BaseDatabaseSchemaEditor from django.db.backends.ddl_references import IndexColumns +from django.db.backends.postgresql.psycopg_any import extensions from django.db.backends.utils import strip_quotes @@ -52,7 +51,7 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor): def quote_value(self, value): if isinstance(value, str): value = value.replace("%", "%%") - adapted = psycopg2.extensions.adapt(value) + adapted = extensions.adapt(value) if hasattr(adapted, "encoding"): adapted.encoding = "utf8" # getquoted() returns a quoted bytestring of the adapted value. diff --git a/tests/backends/postgresql/test_creation.py b/tests/backends/postgresql/test_creation.py index ab2401a07e..bc1e7eb1d8 100644 --- a/tests/backends/postgresql/test_creation.py +++ b/tests/backends/postgresql/test_creation.py @@ -9,12 +9,10 @@ from django.db.backends.base.creation import BaseDatabaseCreation from django.test import SimpleTestCase try: - import psycopg2 # NOQA + from django.db.backends.postgresql.psycopg_any import errors except ImportError: pass else: - from psycopg2 import errors - from django.db.backends.postgresql.creation import DatabaseCreation diff --git a/tests/postgres_tests/test_apps.py b/tests/postgres_tests/test_apps.py index 678e5a9374..7c4cc38183 100644 --- a/tests/postgres_tests/test_apps.py +++ b/tests/postgres_tests/test_apps.py @@ -8,14 +8,18 @@ from django.test import TestCase from django.test.utils import modify_settings try: - from psycopg2.extras import DateRange, DateTimeRange, DateTimeTZRange, NumericRange - from django.contrib.postgres.fields import ( DateRangeField, DateTimeRangeField, DecimalRangeField, IntegerRangeField, ) + from django.db.backends.postgresql.psycopg_any import ( + DateRange, + DateTimeRange, + DateTimeTZRange, + NumericRange, + ) except ImportError: pass diff --git a/tests/postgres_tests/test_array.py b/tests/postgres_tests/test_array.py index e1d9be6c02..a3c26fddae 100644 --- a/tests/postgres_tests/test_array.py +++ b/tests/postgres_tests/test_array.py @@ -31,8 +31,6 @@ from .models import ( ) try: - from psycopg2.extras import NumericRange - from django.contrib.postgres.aggregates import ArrayAgg from django.contrib.postgres.expressions import ArraySubquery from django.contrib.postgres.fields import ArrayField @@ -42,6 +40,7 @@ try: SplitArrayField, SplitArrayWidget, ) + from django.db.backends.postgresql.psycopg_any import NumericRange except ImportError: pass diff --git a/tests/postgres_tests/test_bulk_update.py b/tests/postgres_tests/test_bulk_update.py index f0b473efa7..85dfcedd09 100644 --- a/tests/postgres_tests/test_bulk_update.py +++ b/tests/postgres_tests/test_bulk_update.py @@ -11,9 +11,9 @@ from .models import ( ) try: - from psycopg2.extras import DateRange, NumericRange + from django.db.backends.postgresql.psycopg_any import DateRange, NumericRange except ImportError: - pass # psycopg2 isn't installed. + pass # psycopg isn't installed. class BulkSaveTests(PostgreSQLTestCase): diff --git a/tests/postgres_tests/test_constraints.py b/tests/postgres_tests/test_constraints.py index 9aa5bfdbb8..ad21ffa2b5 100644 --- a/tests/postgres_tests/test_constraints.py +++ b/tests/postgres_tests/test_constraints.py @@ -25,14 +25,13 @@ from . import PostgreSQLTestCase from .models import HotelReservation, IntegerArrayModel, RangesModel, Room, Scene try: - from psycopg2.extras import DateRange, NumericRange - from django.contrib.postgres.constraints import ExclusionConstraint from django.contrib.postgres.fields import ( DateTimeRangeField, RangeBoundary, RangeOperators, ) + from django.db.backends.postgresql.psycopg_any import DateRange, NumericRange except ImportError: pass diff --git a/tests/postgres_tests/test_ranges.py b/tests/postgres_tests/test_ranges.py index 870039a6ad..038e0233c4 100644 --- a/tests/postgres_tests/test_ranges.py +++ b/tests/postgres_tests/test_ranges.py @@ -20,14 +20,17 @@ from .models import ( ) try: - from psycopg2.extras import DateRange, DateTimeTZRange, NumericRange - from django.contrib.postgres import fields as pg_fields from django.contrib.postgres import forms as pg_forms from django.contrib.postgres.validators import ( RangeMaxValueValidator, RangeMinValueValidator, ) + from django.db.backends.postgresql.psycopg_any import ( + DateRange, + DateTimeTZRange, + NumericRange, + ) except ImportError: pass