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:
parent
df45fba3ea
commit
3754b13bec
@ -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]
|
||||
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."
|
||||
|
@ -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,7 +308,18 @@ 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))
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user