|
29
|
1 |
from django.db.backends.postgresql_psycopg2.introspection import DatabaseIntrospection |
|
|
2 |
from django.contrib.gis.gdal import OGRGeomType |
|
|
3 |
|
|
|
4 |
class GeoIntrospectionError(Exception): |
|
|
5 |
pass |
|
|
6 |
|
|
|
7 |
class PostGISIntrospection(DatabaseIntrospection): |
|
|
8 |
# Reverse dictionary for PostGIS geometry types not populated until |
|
|
9 |
# introspection is actually performed. |
|
|
10 |
postgis_types_reverse = {} |
|
|
11 |
|
|
|
12 |
def get_postgis_types(self): |
|
|
13 |
""" |
|
|
14 |
Returns a dictionary with keys that are the PostgreSQL object |
|
|
15 |
identification integers for the PostGIS geometry and/or |
|
|
16 |
geography types (if supported). |
|
|
17 |
""" |
|
|
18 |
cursor = self.connection.cursor() |
|
|
19 |
# The OID integers associated with the geometry type may |
|
|
20 |
# be different across versions; hence, this is why we have |
|
|
21 |
# to query the PostgreSQL pg_type table corresponding to the |
|
|
22 |
# PostGIS custom data types. |
|
|
23 |
oid_sql = 'SELECT "oid" FROM "pg_type" WHERE "typname" = %s' |
|
|
24 |
try: |
|
|
25 |
cursor.execute(oid_sql, ('geometry',)) |
|
|
26 |
GEOM_TYPE = cursor.fetchone()[0] |
|
|
27 |
postgis_types = { GEOM_TYPE : 'GeometryField' } |
|
|
28 |
if self.connection.ops.geography: |
|
|
29 |
cursor.execute(oid_sql, ('geography',)) |
|
|
30 |
GEOG_TYPE = cursor.fetchone()[0] |
|
|
31 |
# The value for the geography type is actually a tuple |
|
|
32 |
# to pass in the `geography=True` keyword to the field |
|
|
33 |
# definition. |
|
|
34 |
postgis_types[GEOG_TYPE] = ('GeometryField', {'geography' : True}) |
|
|
35 |
finally: |
|
|
36 |
cursor.close() |
|
|
37 |
|
|
|
38 |
return postgis_types |
|
|
39 |
|
|
|
40 |
def get_field_type(self, data_type, description): |
|
|
41 |
if not self.postgis_types_reverse: |
|
|
42 |
# If the PostGIS types reverse dictionary is not populated, do so |
|
|
43 |
# now. In order to prevent unnecessary requests upon connection |
|
|
44 |
# intialization, the `data_types_reverse` dictionary is not updated |
|
|
45 |
# with the PostGIS custom types until introspection is actually |
|
|
46 |
# performed -- in other words, when this function is called. |
|
|
47 |
self.postgis_types_reverse = self.get_postgis_types() |
|
|
48 |
self.data_types_reverse.update(self.postgis_types_reverse) |
|
|
49 |
return super(PostGISIntrospection, self).get_field_type(data_type, description) |
|
|
50 |
|
|
|
51 |
def get_geometry_type(self, table_name, geo_col): |
|
|
52 |
""" |
|
|
53 |
The geometry type OID used by PostGIS does not indicate the particular |
|
|
54 |
type of field that a geometry column is (e.g., whether it's a |
|
|
55 |
PointField or a PolygonField). Thus, this routine queries the PostGIS |
|
|
56 |
metadata tables to determine the geometry type, |
|
|
57 |
""" |
|
|
58 |
cursor = self.connection.cursor() |
|
|
59 |
try: |
|
|
60 |
try: |
|
|
61 |
# First seeing if this geometry column is in the `geometry_columns` |
|
|
62 |
cursor.execute('SELECT "coord_dimension", "srid", "type" ' |
|
|
63 |
'FROM "geometry_columns" ' |
|
|
64 |
'WHERE "f_table_name"=%s AND "f_geometry_column"=%s', |
|
|
65 |
(table_name, geo_col)) |
|
|
66 |
row = cursor.fetchone() |
|
|
67 |
if not row: raise GeoIntrospectionError |
|
|
68 |
except GeoIntrospectionError: |
|
|
69 |
if self.connection.ops.geography: |
|
|
70 |
cursor.execute('SELECT "coord_dimension", "srid", "type" ' |
|
|
71 |
'FROM "geography_columns" ' |
|
|
72 |
'WHERE "f_table_name"=%s AND "f_geography_column"=%s', |
|
|
73 |
(table_name, geo_col)) |
|
|
74 |
row = cursor.fetchone() |
|
|
75 |
|
|
|
76 |
if not row: |
|
|
77 |
raise Exception('Could not find a geometry or geography column for "%s"."%s"' % |
|
|
78 |
(table_name, geo_col)) |
|
|
79 |
|
|
|
80 |
# OGRGeomType does not require GDAL and makes it easy to convert |
|
|
81 |
# from OGC geom type name to Django field. |
|
|
82 |
field_type = OGRGeomType(row[2]).django |
|
|
83 |
|
|
|
84 |
# Getting any GeometryField keyword arguments that are not the default. |
|
|
85 |
dim = row[0] |
|
|
86 |
srid = row[1] |
|
|
87 |
field_params = {} |
|
|
88 |
if srid != 4326: |
|
|
89 |
field_params['srid'] = srid |
|
|
90 |
if dim != 2: |
|
|
91 |
field_params['dim'] = dim |
|
|
92 |
finally: |
|
|
93 |
cursor.close() |
|
|
94 |
|
|
|
95 |
return field_type, field_params |