Refs #24928 -- Added introspection support for PostgreSQL HStoreField.

This commit is contained in:
Mariusz Felisiak 2025-11-14 13:36:15 +01:00 committed by GitHub
parent 0eec2a163a
commit 35f86b641a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 37 additions and 2 deletions

View file

@ -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:

View file

@ -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)

View file

@ -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,

View file

@ -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`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View file

@ -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)"
):

View file

@ -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)",
],
)