summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJustin Bronn <jbronn@gmail.com>2007-07-26 02:18:26 +0000
committerJustin Bronn <jbronn@gmail.com>2007-07-26 02:18:26 +0000
commit3754b13bec37afab712c52ab15e56fae5a25bac4 (patch)
treebf987d339c901c064c5f92fbdea59f9e3e560df6
parentdf45fba3ea521770d4f0d7dbe35eb8736aec1835 (diff)
gis: geographic queries may now take geos geometry objects as an argument and geometry objects with different srid's are automatically transformed to the srid of the field; synced up parse_lookup() and lookup_inner() to r5609; minor change in GeometryProxy.
git-svn-id: http://code.djangoproject.com/svn/django/branches/gis@5762 bcc190cf-cafb-0310-a4f2-bffc1f526a37
-rw-r--r--django/contrib/gis/db/models/fields/__init__.py44
-rw-r--r--django/contrib/gis/db/models/postgis.py33
-rw-r--r--django/contrib/gis/db/models/proxy.py2
3 files changed, 64 insertions, 15 deletions
diff --git a/django/contrib/gis/db/models/fields/__init__.py b/django/contrib/gis/db/models/fields/__init__.py
index 41d26c58b8..6f2856d3d9 100644
--- a/django/contrib/gis/db/models/fields/__init__.py
+++ b/django/contrib/gis/db/models/fields/__init__.py
@@ -5,10 +5,9 @@ from django.contrib.gis.db.models.postgis import POSTGIS_TERMS, quotename
from django.contrib.gis.oldforms import WKTField
from django.utils.functional import curry
from django.contrib.gis.geos import GEOSGeometry, GEOSException
+from types import StringType
#TODO: Flesh out widgets.
-#TODO: GEOS and GDAL/OGR operations through fields as proxy.
-#TODO: pythonic usage, like "for point in zip.polygon" and "if point in polygon".
class GeometryField(Field):
"The base GIS field -- maps to the OpenGIS Specification Geometry type."
@@ -114,15 +113,44 @@ class GeometryField(Field):
return "NoField"
def get_db_prep_lookup(self, lookup_type, value):
- """Returns field's value prepared for database lookup; the SRID of the geometry is
- included by default in these queries."""
+ "Returns field's value prepared for database lookup, accepts WKT and GEOS Geometries for the value."
+ if not bool(value): return None
if lookup_type in POSTGIS_TERMS:
- return [value and ("SRID=%d;%s" % (self._srid, value)) or None]
- raise TypeError("Field has invalid lookup: %s" % lookup_type)
+ if isinstance(value, GEOSGeometry):
+ # GEOSGeometry instance passed in.
+ if value.srid != self._srid:
+ # Returning a dictionary instructs the parse_lookup() to add what's in the 'where' key
+ # to the where parameters, since we need to transform the geometry in the query.
+ return {'where' : "Transform(%s,%s)",
+ 'params' : [value, self._srid]
+ }
+ else:
+ # Just return the GEOSGeometry, it has its own psycopg2 adaptor.
+ return [value]
+ elif isinstance(value, StringType):
+ # String instance passed in, assuming WKT.
+ # TODO: Any validation needed here to prevent SQL injection?
+ return ["SRID=%d;%s" % (self._srid, value)]
+ else:
+ raise TypeError("Invalid type (%s) used for field lookup value." % str(type(value)))
+ else:
+ raise TypeError("Field has invalid lookup: %s" % lookup_type)
def get_db_prep_save(self, value):
- "Making sure the SRID is included before saving."
- return value and ("SRID=%d;%s" % (self._srid, value)) or None
+ "Prepares the value for saving in the database."
+ if not bool(value): return None
+ if isinstance(value, GEOSGeometry):
+ return value
+ else:
+ return ("SRID=%d;%s" % (self._srid, wkt))
+
+ def get_placeholder(self, value):
+ "Provides a proper substitution value for "
+ if isinstance(value, GEOSGeometry) and value.srid != self._srid:
+ # Adding Transform() to the SQL placeholder.
+ return 'Transform(%%s, %s)' % self._srid
+ else:
+ return '%s'
def get_manipulator_field_objs(self):
"Using the WKTField (defined above) to be our manipulator."
diff --git a/django/contrib/gis/db/models/postgis.py b/django/contrib/gis/db/models/postgis.py
index 776181624c..17914f5114 100644
--- a/django/contrib/gis/db/models/postgis.py
+++ b/django/contrib/gis/db/models/postgis.py
@@ -1,7 +1,7 @@
# This module is meant to re-define the helper routines used by the
# django.db.models.query objects to be customized for PostGIS.
from django.db import backend
-from django.db.models.query import LOOKUP_SEPARATOR, find_field, FieldFound, QUERY_TERMS, get_where_clause
+from django.db.models.query import LOOKUP_SEPARATOR, field_choices, find_field, FieldFound, QUERY_TERMS, get_where_clause
from django.utils.datastructures import SortedDict
# PostGIS-specific operators. The commented descriptions of these
@@ -84,6 +84,12 @@ def get_geo_where_clause(lookup_type, table_prefix, field_name, value):
raise TypeError, "Got invalid lookup_type: %s" % repr(lookup_type)
+#### query.py overloaded functions ####
+# parse_lookup() and lookup_inner() are modified from their django/db/models/query.py
+# counterparts to support constructing SQL for geographic queries.
+#
+# Status: Synced with r5609.
+#
def parse_lookup(kwarg_items, opts):
# Helper function that handles converting API kwargs
# (e.g. "name__exact": "tom") to SQL.
@@ -117,7 +123,6 @@ def parse_lookup(kwarg_items, opts):
# If there is only one part, or the last part is not a query
# term, assume that the query is an __exact
lookup_type = path.pop()
-
if lookup_type == 'pk':
lookup_type = 'exact'
path.append(None)
@@ -133,6 +138,8 @@ def parse_lookup(kwarg_items, opts):
# all uses of None as a query value.
if lookup_type != 'exact':
raise ValueError, "Cannot use None as a query value"
+ elif callable(value):
+ value = value()
joins2, where2, params2 = lookup_inner(path, lookup_type, value, opts, opts.db_table, None)
joins.update(joins2)
@@ -206,7 +213,6 @@ def lookup_inner(path, lookup_type, value, opts, table, column):
# Does the name belong to a one-to-one, many-to-one, or regular field?
field = find_field(name, current_opts.fields, False)
-
if field:
if field.rel: # One-to-One/Many-to-one field
new_table = current_table + '__' + name
@@ -225,7 +231,11 @@ def lookup_inner(path, lookup_type, value, opts, table, column):
except FieldFound: # Match found, loop has been shortcut.
pass
else: # No match found.
- raise TypeError, "Cannot resolve keyword '%s' into field" % name
+ choices = field_choices(current_opts.many_to_many, False) + \
+ field_choices(current_opts.get_all_related_many_to_many_objects(), True) + \
+ field_choices(current_opts.get_all_related_objects(), True) + \
+ field_choices(current_opts.fields, False)
+ raise TypeError, "Cannot resolve keyword '%s' into field. Choices are: %s" % (name, ", ".join(choices))
# Check whether an intermediate join is required between current_table
# and new_table.
@@ -298,9 +308,20 @@ def lookup_inner(path, lookup_type, value, opts, table, column):
# If the field is a geometry field, then the WHERE clause will need to be obtained
# with the get_geo_where_clause()
if hasattr(field, '_geom'):
- where.append(get_geo_where_clause(lookup_type, current_table + '.', column, value))
+ # Getting the geographic where clause.
+ gwc = get_geo_where_clause(lookup_type, current_table + '.', column, value)
+
+ # Getting the geographic parameters from the field.
+ geo_params = field.get_db_prep_lookup(lookup_type, value)
+
+ # If a dictionary was passed back from the field modify the where clause.
+ if isinstance(geo_params, dict):
+ gwc = gwc % geo_params['where']
+ geo_params = geo_params['params']
+ where.append(gwc)
+ params.extend(geo_params)
else:
where.append(get_where_clause(lookup_type, current_table + '.', column, value))
- params.extend(field.get_db_prep_lookup(lookup_type, value))
+ params.extend(field.get_db_prep_lookup(lookup_type, value))
return joins, where, params
diff --git a/django/contrib/gis/db/models/proxy.py b/django/contrib/gis/db/models/proxy.py
index 4762e8b18f..de366d0186 100644
--- a/django/contrib/gis/db/models/proxy.py
+++ b/django/contrib/gis/db/models/proxy.py
@@ -24,7 +24,7 @@ class GeometryProxy(object):
def __set__(self, obj, value):
if isinstance(value, GEOSGeometry):
if value and ((value.srid is None) and (self._field._srid is not None)):
- value.set_srid(self._field._srid)
+ value.srid = self._field._srid
obj.__dict__[self._field.attname] = value
return value