diff --git a/django/contrib/gis/db/backends/postgis/introspection.py b/django/contrib/gis/db/backends/postgis/introspection.py index 66c5bb5d42..0647012b04 100644 --- a/django/contrib/gis/db/backends/postgis/introspection.py +++ b/django/contrib/gis/db/backends/postgis/introspection.py @@ -3,9 +3,7 @@ from django.db.backends.postgresql.introspection import DatabaseIntrospection class PostGISIntrospection(DatabaseIntrospection): - # Reverse dictionary for PostGIS geometry types not populated until - # introspection is actually performed. - postgis_types_reverse = {} + postgis_oid_lookup = {} # Populated when introspection is performed. ignored_tables = DatabaseIntrospection.ignored_tables + [ 'geography_columns', @@ -15,42 +13,17 @@ class PostGISIntrospection(DatabaseIntrospection): 'raster_overviews', ] - def get_postgis_types(self): - """ - Return a dictionary with keys that are the PostgreSQL object - identification integers for the PostGIS geometry and/or - geography types (if supported). - """ - field_types = [ - ('geometry', 'GeometryField'), - # The value for the geography type is actually a tuple - # to pass in the `geography=True` keyword to the field - # definition. - ('geography', ('GeometryField', {'geography': True})), - ] - postgis_types = {} - - # The OID integers associated with the geometry type may - # be different across versions; hence, this is why we have - # to query the PostgreSQL pg_type table corresponding to the - # PostGIS custom data types. - oid_sql = 'SELECT "oid" FROM "pg_type" WHERE "typname" = %s' - with self.connection.cursor() as cursor: - for field_type in field_types: - cursor.execute(oid_sql, (field_type[0],)) - for result in cursor.fetchall(): - postgis_types[result[0]] = field_type[1] - return postgis_types - def get_field_type(self, data_type, description): - if not self.postgis_types_reverse: - # If the PostGIS types reverse dictionary is not populated, do so - # now. In order to prevent unnecessary requests upon connection - # initialization, the `data_types_reverse` dictionary is not updated - # with the PostGIS custom types until introspection is actually - # performed -- in other words, when this function is called. - self.postgis_types_reverse = self.get_postgis_types() - self.data_types_reverse.update(self.postgis_types_reverse) + if not self.postgis_oid_lookup: + # Query PostgreSQL's pg_type table to determine the OID integers + # for the PostGIS data types used in reverse lookup (the integers + # may be different across versions). To prevent unnecessary + # requests upon connection initialization, the `data_types_reverse` + # dictionary isn't updated until introspection is performed here. + with self.connection.cursor() as cursor: + cursor.execute("SELECT oid, typname FROM pg_type WHERE typname IN ('geometry', 'geography')") + self.postgis_oid_lookup = dict(cursor.fetchall()) + self.data_types_reverse.update((oid, 'GeometryField') for oid in self.postgis_oid_lookup) return super().get_field_type(data_type, description) def get_geometry_type(self, table_name, description): @@ -78,6 +51,8 @@ class PostGISIntrospection(DatabaseIntrospection): field_type = OGRGeomType(field_type).django # Getting any GeometryField keyword arguments that are not the default. field_params = {} + if self.postgis_oid_lookup.get(description.type_code) == 'geography': + field_params['geography'] = True if srid != 4326: field_params['srid'] = srid if dim != 2: