diff -r b758351d191f -r cc9b7e14412b web/lib/django/contrib/gis/db/backends/postgis/introspection.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/lib/django/contrib/gis/db/backends/postgis/introspection.py Tue May 25 02:43:45 2010 +0200 @@ -0,0 +1,95 @@ +from django.db.backends.postgresql_psycopg2.introspection import DatabaseIntrospection +from django.contrib.gis.gdal import OGRGeomType + +class GeoIntrospectionError(Exception): + pass + +class PostGISIntrospection(DatabaseIntrospection): + # Reverse dictionary for PostGIS geometry types not populated until + # introspection is actually performed. + postgis_types_reverse = {} + + def get_postgis_types(self): + """ + Returns a dictionary with keys that are the PostgreSQL object + identification integers for the PostGIS geometry and/or + geography types (if supported). + """ + cursor = self.connection.cursor() + # 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' + try: + cursor.execute(oid_sql, ('geometry',)) + GEOM_TYPE = cursor.fetchone()[0] + postgis_types = { GEOM_TYPE : 'GeometryField' } + if self.connection.ops.geography: + cursor.execute(oid_sql, ('geography',)) + GEOG_TYPE = cursor.fetchone()[0] + # The value for the geography type is actually a tuple + # to pass in the `geography=True` keyword to the field + # definition. + postgis_types[GEOG_TYPE] = ('GeometryField', {'geography' : True}) + finally: + cursor.close() + + 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 + # intialization, 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) + return super(PostGISIntrospection, self).get_field_type(data_type, description) + + def get_geometry_type(self, table_name, geo_col): + """ + The geometry type OID used by PostGIS does not indicate the particular + type of field that a geometry column is (e.g., whether it's a + PointField or a PolygonField). Thus, this routine queries the PostGIS + metadata tables to determine the geometry type, + """ + cursor = self.connection.cursor() + try: + try: + # First seeing if this geometry column is in the `geometry_columns` + cursor.execute('SELECT "coord_dimension", "srid", "type" ' + 'FROM "geometry_columns" ' + 'WHERE "f_table_name"=%s AND "f_geometry_column"=%s', + (table_name, geo_col)) + row = cursor.fetchone() + if not row: raise GeoIntrospectionError + except GeoIntrospectionError: + if self.connection.ops.geography: + cursor.execute('SELECT "coord_dimension", "srid", "type" ' + 'FROM "geography_columns" ' + 'WHERE "f_table_name"=%s AND "f_geography_column"=%s', + (table_name, geo_col)) + row = cursor.fetchone() + + if not row: + raise Exception('Could not find a geometry or geography column for "%s"."%s"' % + (table_name, geo_col)) + + # OGRGeomType does not require GDAL and makes it easy to convert + # from OGC geom type name to Django field. + field_type = OGRGeomType(row[2]).django + + # Getting any GeometryField keyword arguments that are not the default. + dim = row[0] + srid = row[1] + field_params = {} + if srid != 4326: + field_params['srid'] = srid + if dim != 2: + field_params['dim'] = dim + finally: + cursor.close() + + return field_type, field_params