1
0
mirror of https://github.com/django/django.git synced 2025-07-04 09:49:12 +00:00

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
This commit is contained in:
Justin Bronn 2007-07-26 02:18:26 +00:00
parent df45fba3ea
commit 3754b13bec
3 changed files with 64 additions and 15 deletions

View File

@ -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."

View File

@ -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

View File

@ -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