web/lib/django/contrib/gis/db/backends/postgis/introspection.py
changeset 29 cc9b7e14412b
--- /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