Skip to content

Commit 35f86b6

Browse files
authored
Refs #24928 -- Added introspection support for PostgreSQL HStoreField.
1 parent 0eec2a1 commit 35f86b6

6 files changed

Lines changed: 37 additions & 2 deletions

File tree

django/contrib/postgres/apps.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,11 @@ def ready(self):
6565
3910: "django.contrib.postgres.fields.DateTimeRangeField",
6666
3912: "django.contrib.postgres.fields.DateRangeField",
6767
3926: "django.contrib.postgres.fields.BigIntegerRangeField",
68+
# PostgreSQL OIDs may vary depending on the
69+
# installation, especially for datatypes from
70+
# extensions, e.g. "hstore". In such cases, the
71+
# type_display attribute (psycopg 3.2+) should be used.
72+
"hstore": "django.contrib.postgres.fields.HStoreField",
6873
}
6974
)
7075
if conn.connection is not None:

django/db/backends/postgresql/base.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import threading
99
import warnings
1010
from contextlib import contextmanager
11+
from functools import lru_cache
1112

1213
from django.conf import settings
1314
from django.core.exceptions import ImproperlyConfigured
@@ -29,6 +30,7 @@
2930
raise ImproperlyConfigured("Error loading psycopg2 or psycopg module")
3031

3132

33+
@lru_cache
3234
def psycopg_version():
3335
version = Database.__version__.split(" ", 1)[0]
3436
return get_version_tuple(version)

django/db/backends/postgresql/introspection.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from django.db.backends.base.introspection import BaseDatabaseIntrospection
44
from django.db.backends.base.introspection import FieldInfo as BaseFieldInfo
55
from django.db.backends.base.introspection import TableInfo as BaseTableInfo
6+
from django.db.backends.postgresql.base import psycopg_version
67
from django.db.models import DB_CASCADE, DB_SET_DEFAULT, DB_SET_NULL, DO_NOTHING, Index
78

89
FieldInfo = namedtuple("FieldInfo", [*BaseFieldInfo._fields, "is_autofield", "comment"])
@@ -120,10 +121,19 @@ def get_table_description(self, cursor, table_name):
120121
cursor.execute(
121122
"SELECT * FROM %s LIMIT 1" % self.connection.ops.quote_name(table_name)
122123
)
124+
125+
# PostgreSQL OIDs may vary depending on the installation, especially
126+
# for datatypes from extensions, e.g. "hstore". In such cases, the
127+
# type_display attribute (psycopg 3.2+) should be used.
128+
type_display_available = psycopg_version() >= (3, 2)
123129
return [
124130
FieldInfo(
125131
line.name,
126-
line.type_code,
132+
(
133+
line.type_display
134+
if type_display_available and line.type_display == "hstore"
135+
else line.type_code
136+
),
127137
# display_size is always None on psycopg2.
128138
line.internal_size if line.display_size is None else line.display_size,
129139
line.internal_size,

docs/releases/6.1.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,9 @@ Minor features
126126
:mod:`django.contrib.postgres`
127127
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
128128

129-
* ...
129+
* :djadmin:`inspectdb` now introspects
130+
:class:`~django.contrib.postgres.fields.HStoreField` when ``psycopg`` 3.2+ is
131+
installed and ``django.contrib.postgres`` is in :setting:`INSTALLED_APPS`.
130132

131133
:mod:`django.contrib.redirects`
132134
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

tests/backends/postgresql/tests.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -517,8 +517,11 @@ def test_lookup_cast_isnull_noop(self):
517517
def test_correct_extraction_psycopg_version(self):
518518
from django.db.backends.postgresql.base import Database, psycopg_version
519519

520+
psycopg_version.cache_clear()
520521
with mock.patch.object(Database, "__version__", "4.2.1 (dt dec pq3 ext lo64)"):
522+
self.addCleanup(psycopg_version.cache_clear)
521523
self.assertEqual(psycopg_version(), (4, 2, 1))
524+
psycopg_version.cache_clear()
522525
with mock.patch.object(
523526
Database, "__version__", "4.2b0.dev1 (dt dec pq3 ext lo64)"
524527
):

tests/postgres_tests/test_introspection.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,16 @@ def test_range_fields(self):
3333
"null=True)",
3434
],
3535
)
36+
37+
def test_hstore_field(self):
38+
from django.db.backends.postgresql.base import psycopg_version
39+
40+
if psycopg_version() < (3, 2):
41+
self.skipTest("psycopg 3.2+ is required.")
42+
self.assertFieldsInModel(
43+
"postgres_tests_hstoremodel",
44+
[
45+
"field = django.contrib.postgres.fields.HStoreField(blank=True, "
46+
"null=True)",
47+
],
48+
)

0 commit comments

Comments
 (0)