1
+ from types import StringType , UnicodeType
2
+ from django .db import connection
1
3
from django .db .models .fields import Field # Django base Field class
2
4
from django .contrib .gis .geos import GEOSGeometry , GEOSException
3
- from django .contrib .gis .db .backend .postgis .query import POSTGIS_TERMS , geo_quotename as quotename
4
- from types import StringType
5
+ from django .contrib .gis .db .backend .util import GeoFieldSQL
6
+ from django .contrib .gis .db .backend .postgis .adaptor import PostGISAdaptor
7
+ from django .contrib .gis .db .backend .postgis .query import POSTGIS_TERMS , TRANSFORM
8
+ from psycopg2 import Binary
9
+
10
+ # Quotename & geographic quotename, respectively
11
+ qn = connection .ops .quote_name
12
+ def gqn (value ):
13
+ if isinstance (value , UnicodeType ): value = value .encode ('ascii' )
14
+ return "'%s'" % value
5
15
6
16
class PostGISField (Field ):
7
17
def _add_geom (self , style , db_table ):
@@ -10,36 +20,36 @@ def _add_geom(self, style, db_table):
10
20
AddGeometryColumn(...) PostGIS (and OGC standard) stored procedure.
11
21
12
22
Takes the style object (provides syntax highlighting) and the
13
- database table as parameters.
23
+ database table as parameters.
14
24
"""
15
25
sql = style .SQL_KEYWORD ('SELECT ' ) + \
16
26
style .SQL_TABLE ('AddGeometryColumn' ) + '(' + \
17
- style .SQL_TABLE (quotename (db_table )) + ', ' + \
18
- style .SQL_FIELD (quotename (self .column )) + ', ' + \
27
+ style .SQL_TABLE (gqn (db_table )) + ', ' + \
28
+ style .SQL_FIELD (gqn (self .column )) + ', ' + \
19
29
style .SQL_FIELD (str (self ._srid )) + ', ' + \
20
- style .SQL_COLTYPE (quotename (self ._geom )) + ', ' + \
30
+ style .SQL_COLTYPE (gqn (self ._geom )) + ', ' + \
21
31
style .SQL_KEYWORD (str (self ._dim )) + ');'
22
32
23
33
if not self .null :
24
34
# Add a NOT NULL constraint to the field
25
35
sql += '\n ' + \
26
36
style .SQL_KEYWORD ('ALTER TABLE ' ) + \
27
- style .SQL_TABLE (quotename (db_table , dbl = True )) + \
37
+ style .SQL_TABLE (qn (db_table )) + \
28
38
style .SQL_KEYWORD (' ALTER ' ) + \
29
- style .SQL_FIELD (quotename (self .column , dbl = True )) + \
39
+ style .SQL_FIELD (qn (self .column )) + \
30
40
style .SQL_KEYWORD (' SET NOT NULL' ) + ';'
31
41
return sql
32
42
33
43
def _geom_index (self , style , db_table ,
34
44
index_type = 'GIST' , index_opts = 'GIST_GEOMETRY_OPS' ):
35
45
"Creates a GiST index for this geometry field."
36
46
sql = style .SQL_KEYWORD ('CREATE INDEX ' ) + \
37
- style .SQL_TABLE (quotename ('%s_%s_id' % (db_table , self .column ), dbl = True )) + \
47
+ style .SQL_TABLE (qn ('%s_%s_id' % (db_table , self .column ))) + \
38
48
style .SQL_KEYWORD (' ON ' ) + \
39
- style .SQL_TABLE (quotename (db_table , dbl = True )) + \
49
+ style .SQL_TABLE (qn (db_table )) + \
40
50
style .SQL_KEYWORD (' USING ' ) + \
41
51
style .SQL_COLTYPE (index_type ) + ' ( ' + \
42
- style .SQL_FIELD (quotename (self .column , dbl = True )) + ' ' + \
52
+ style .SQL_FIELD (qn (self .column )) + ' ' + \
43
53
style .SQL_KEYWORD (index_opts ) + ' );'
44
54
return sql
45
55
@@ -64,8 +74,8 @@ def _post_delete_sql(self, style, db_table):
64
74
"Drops the geometry column."
65
75
sql = style .SQL_KEYWORD ('SELECT ' ) + \
66
76
style .SQL_KEYWORD ('DropGeometryColumn' ) + '(' + \
67
- style .SQL_TABLE (quotename (db_table )) + ', ' + \
68
- style .SQL_FIELD (quotename (self .column )) + ');'
77
+ style .SQL_TABLE (gqn (db_table )) + ', ' + \
78
+ style .SQL_FIELD (gqn (self .column )) + ');'
69
79
return sql
70
80
71
81
def db_type (self ):
@@ -81,51 +91,63 @@ def get_db_prep_lookup(self, lookup_type, value):
81
91
GEOS Geometries for the value.
82
92
"""
83
93
if lookup_type in POSTGIS_TERMS :
84
- if lookup_type == 'isnull' : return [value ] # special case for NULL geometries.
85
- if not bool (value ): return [None ] # If invalid value passed in.
94
+ # special case for isnull lookup
95
+ if lookup_type == 'isnull' :
96
+ return GeoFieldSQL ([], [value ])
97
+
98
+ # When the input is not a GEOS geometry, attempt to construct one
99
+ # from the given string input.
86
100
if isinstance (value , GEOSGeometry ):
87
- # GEOSGeometry instance passed in.
88
- if value .srid != self ._srid :
89
- # Returning a dictionary instructs the parse_lookup() to add
90
- # what's in the 'where' key to the where parameters, since we
91
- # need to transform the geometry in the query.
92
- return {'where' : ["ST_Transform(%s,%s)" ],
93
- 'params' : [value , self ._srid ]
94
- }
95
- else :
96
- # Just return the GEOSGeometry, it has its own psycopg2 adaptor.
97
- return [value ]
98
- elif isinstance (value , StringType ):
99
- # String instance passed in, assuming WKT.
100
- # TODO: Any validation needed here to prevent SQL injection?
101
- return ["SRID=%d;%s" % (self ._srid , value )]
101
+ pass
102
+ elif isinstance (value , (StringType , UnicodeType )):
103
+ try :
104
+ value = GEOSGeometry (value )
105
+ except GEOSException :
106
+ raise TypeError ("Could not create geometry from lookup value: %s" % str (value ))
107
+ else :
108
+ raise TypeError ('Cannot use parameter of %s type as lookup parameter.' % type (value ))
109
+
110
+ # Getting the SRID of the geometry, or defaulting to that of the field if
111
+ # it is None.
112
+ if value .srid is None : srid = self ._srid
113
+ else : srid = value .srid
114
+
115
+ # The adaptor will be used by psycopg2 for quoting the WKB.
116
+ adapt = PostGISAdaptor (value , srid )
117
+
118
+ if srid != self ._srid :
119
+ # Adding the necessary string substitutions and parameters
120
+ # to perform a geometry transformation.
121
+ return GeoFieldSQL (['%s(%%s,%%s)' % TRANSFORM ],
122
+ [adapt , self ._srid ])
102
123
else :
103
- raise TypeError ( "Invalid type (%s) used for field lookup value." % str ( type ( value )) )
124
+ return GeoFieldSQL ([ '%s' ], [ adapt ] )
104
125
else :
105
126
raise TypeError ("Field has invalid lookup: %s" % lookup_type )
106
127
107
128
def get_db_prep_save (self , value ):
108
129
"Prepares the value for saving in the database."
109
130
if not bool (value ): return None
110
131
if isinstance (value , GEOSGeometry ):
111
- return value
132
+ return PostGISAdaptor ( value , value . srid )
112
133
else :
113
134
raise TypeError ('Geometry Proxy should only return GEOSGeometry objects.' )
114
135
115
136
def get_internal_type (self ):
116
137
"""
117
- Returns NoField because a stored procedure is used by PostGIS to create the
138
+ Returns NoField because a stored procedure is used by PostGIS to create
139
+ the Geometry Fields.
118
140
"""
119
141
return 'NoField'
120
142
121
143
def get_placeholder (self , value ):
122
144
"""
123
145
Provides a proper substitution value for Geometries that are not in the
124
- SRID of the field. Specifically, this routine will substitute in the
125
- ST_Transform() function call.
146
+ SRID of the field. Specifically, this routine will substitute in the
147
+ ST_Transform() function call.
126
148
"""
127
149
if isinstance (value , GEOSGeometry ) and value .srid != self ._srid :
128
150
# Adding Transform() to the SQL placeholder.
129
- return 'ST_Transform (%%s, %s)' % self ._srid
151
+ return '%s (%%s, %s)' % ( TRANSFORM , self ._srid )
130
152
else :
131
153
return '%s'
0 commit comments