summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--django/contrib/postgres/apps.py5
-rw-r--r--django/db/backends/postgresql/base.py2
-rw-r--r--django/db/backends/postgresql/introspection.py12
-rw-r--r--docs/releases/6.1.txt4
-rw-r--r--tests/backends/postgresql/tests.py3
-rw-r--r--tests/postgres_tests/test_introspection.py13
6 files changed, 37 insertions, 2 deletions
diff --git a/django/contrib/postgres/apps.py b/django/contrib/postgres/apps.py
index a8ee3fbf4b..63fd9b274e 100644
--- a/django/contrib/postgres/apps.py
+++ b/django/contrib/postgres/apps.py
@@ -65,6 +65,11 @@ class PostgresConfig(AppConfig):
3910: "django.contrib.postgres.fields.DateTimeRangeField",
3912: "django.contrib.postgres.fields.DateRangeField",
3926: "django.contrib.postgres.fields.BigIntegerRangeField",
+ # PostgreSQL OIDs may vary depending on the
+ # installation, especially for datatypes from
+ # extensions, e.g. "hstore". In such cases, the
+ # type_display attribute (psycopg 3.2+) should be used.
+ "hstore": "django.contrib.postgres.fields.HStoreField",
}
)
if conn.connection is not None:
diff --git a/django/db/backends/postgresql/base.py b/django/db/backends/postgresql/base.py
index 17ac417fff..e2728afea5 100644
--- a/django/db/backends/postgresql/base.py
+++ b/django/db/backends/postgresql/base.py
@@ -8,6 +8,7 @@ import asyncio
import threading
import warnings
from contextlib import contextmanager
+from functools import lru_cache
from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
@@ -29,6 +30,7 @@ except ImportError:
raise ImproperlyConfigured("Error loading psycopg2 or psycopg module")
+@lru_cache
def psycopg_version():
version = Database.__version__.split(" ", 1)[0]
return get_version_tuple(version)
diff --git a/django/db/backends/postgresql/introspection.py b/django/db/backends/postgresql/introspection.py
index 30edaf10da..791d729ea4 100644
--- a/django/db/backends/postgresql/introspection.py
+++ b/django/db/backends/postgresql/introspection.py
@@ -3,6 +3,7 @@ from collections import namedtuple
from django.db.backends.base.introspection import BaseDatabaseIntrospection
from django.db.backends.base.introspection import FieldInfo as BaseFieldInfo
from django.db.backends.base.introspection import TableInfo as BaseTableInfo
+from django.db.backends.postgresql.base import psycopg_version
from django.db.models import DB_CASCADE, DB_SET_DEFAULT, DB_SET_NULL, DO_NOTHING, Index
FieldInfo = namedtuple("FieldInfo", [*BaseFieldInfo._fields, "is_autofield", "comment"])
@@ -120,10 +121,19 @@ class DatabaseIntrospection(BaseDatabaseIntrospection):
cursor.execute(
"SELECT * FROM %s LIMIT 1" % self.connection.ops.quote_name(table_name)
)
+
+ # PostgreSQL OIDs may vary depending on the installation, especially
+ # for datatypes from extensions, e.g. "hstore". In such cases, the
+ # type_display attribute (psycopg 3.2+) should be used.
+ type_display_available = psycopg_version() >= (3, 2)
return [
FieldInfo(
line.name,
- line.type_code,
+ (
+ line.type_display
+ if type_display_available and line.type_display == "hstore"
+ else line.type_code
+ ),
# display_size is always None on psycopg2.
line.internal_size if line.display_size is None else line.display_size,
line.internal_size,
diff --git a/docs/releases/6.1.txt b/docs/releases/6.1.txt
index e0edf6876a..8c5594d5b6 100644
--- a/docs/releases/6.1.txt
+++ b/docs/releases/6.1.txt
@@ -126,7 +126,9 @@ Minor features
:mod:`django.contrib.postgres`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-* ...
+* :djadmin:`inspectdb` now introspects
+ :class:`~django.contrib.postgres.fields.HStoreField` when ``psycopg`` 3.2+ is
+ installed and ``django.contrib.postgres`` is in :setting:`INSTALLED_APPS`.
:mod:`django.contrib.redirects`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/tests/backends/postgresql/tests.py b/tests/backends/postgresql/tests.py
index f7e7d1e68c..e03c37cb57 100644
--- a/tests/backends/postgresql/tests.py
+++ b/tests/backends/postgresql/tests.py
@@ -517,8 +517,11 @@ class Tests(TestCase):
def test_correct_extraction_psycopg_version(self):
from django.db.backends.postgresql.base import Database, psycopg_version
+ psycopg_version.cache_clear()
with mock.patch.object(Database, "__version__", "4.2.1 (dt dec pq3 ext lo64)"):
+ self.addCleanup(psycopg_version.cache_clear)
self.assertEqual(psycopg_version(), (4, 2, 1))
+ psycopg_version.cache_clear()
with mock.patch.object(
Database, "__version__", "4.2b0.dev1 (dt dec pq3 ext lo64)"
):
diff --git a/tests/postgres_tests/test_introspection.py b/tests/postgres_tests/test_introspection.py
index 73c426d1ba..1ae940fdb9 100644
--- a/tests/postgres_tests/test_introspection.py
+++ b/tests/postgres_tests/test_introspection.py
@@ -33,3 +33,16 @@ class InspectDBTests(PostgreSQLTestCase):
"null=True)",
],
)
+
+ def test_hstore_field(self):
+ from django.db.backends.postgresql.base import psycopg_version
+
+ if psycopg_version() < (3, 2):
+ self.skipTest("psycopg 3.2+ is required.")
+ self.assertFieldsInModel(
+ "postgres_tests_hstoremodel",
+ [
+ "field = django.contrib.postgres.fields.HStoreField(blank=True, "
+ "null=True)",
+ ],
+ )