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.contrib.gis.oldforms import WKTField
|
||||||
from django.utils.functional import curry
|
from django.utils.functional import curry
|
||||||
from django.contrib.gis.geos import GEOSGeometry, GEOSException
|
from django.contrib.gis.geos import GEOSGeometry, GEOSException
|
||||||
|
from types import StringType
|
||||||
|
|
||||||
#TODO: Flesh out widgets.
|
#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):
|
class GeometryField(Field):
|
||||||
"The base GIS field -- maps to the OpenGIS Specification Geometry type."
|
"The base GIS field -- maps to the OpenGIS Specification Geometry type."
|
||||||
@ -114,15 +113,44 @@ class GeometryField(Field):
|
|||||||
return "NoField"
|
return "NoField"
|
||||||
|
|
||||||
def get_db_prep_lookup(self, lookup_type, value):
|
def get_db_prep_lookup(self, lookup_type, value):
|
||||||
"""Returns field's value prepared for database lookup; the SRID of the geometry is
|
"Returns field's value prepared for database lookup, accepts WKT and GEOS Geometries for the value."
|
||||||
included by default in these queries."""
|
if not bool(value): return None
|
||||||
if lookup_type in POSTGIS_TERMS:
|
if lookup_type in POSTGIS_TERMS:
|
||||||
return [value and ("SRID=%d;%s" % (self._srid, value)) or None]
|
if isinstance(value, GEOSGeometry):
|
||||||
raise TypeError("Field has invalid lookup: %s" % lookup_type)
|
# 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):
|
def get_db_prep_save(self, value):
|
||||||
"Making sure the SRID is included before saving."
|
"Prepares the value for saving in the database."
|
||||||
return value and ("SRID=%d;%s" % (self._srid, value)) or None
|
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):
|
def get_manipulator_field_objs(self):
|
||||||
"Using the WKTField (defined above) to be our manipulator."
|
"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
|
# This module is meant to re-define the helper routines used by the
|
||||||
# django.db.models.query objects to be customized for PostGIS.
|
# django.db.models.query objects to be customized for PostGIS.
|
||||||
from django.db import backend
|
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
|
from django.utils.datastructures import SortedDict
|
||||||
|
|
||||||
# PostGIS-specific operators. The commented descriptions of these
|
# 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)
|
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):
|
def parse_lookup(kwarg_items, opts):
|
||||||
# Helper function that handles converting API kwargs
|
# Helper function that handles converting API kwargs
|
||||||
# (e.g. "name__exact": "tom") to SQL.
|
# (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
|
# If there is only one part, or the last part is not a query
|
||||||
# term, assume that the query is an __exact
|
# term, assume that the query is an __exact
|
||||||
lookup_type = path.pop()
|
lookup_type = path.pop()
|
||||||
|
|
||||||
if lookup_type == 'pk':
|
if lookup_type == 'pk':
|
||||||
lookup_type = 'exact'
|
lookup_type = 'exact'
|
||||||
path.append(None)
|
path.append(None)
|
||||||
@ -133,6 +138,8 @@ def parse_lookup(kwarg_items, opts):
|
|||||||
# all uses of None as a query value.
|
# all uses of None as a query value.
|
||||||
if lookup_type != 'exact':
|
if lookup_type != 'exact':
|
||||||
raise ValueError, "Cannot use None as a query value"
|
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)
|
joins2, where2, params2 = lookup_inner(path, lookup_type, value, opts, opts.db_table, None)
|
||||||
joins.update(joins2)
|
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?
|
# Does the name belong to a one-to-one, many-to-one, or regular field?
|
||||||
field = find_field(name, current_opts.fields, False)
|
field = find_field(name, current_opts.fields, False)
|
||||||
|
|
||||||
if field:
|
if field:
|
||||||
if field.rel: # One-to-One/Many-to-one field
|
if field.rel: # One-to-One/Many-to-one field
|
||||||
new_table = current_table + '__' + name
|
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.
|
except FieldFound: # Match found, loop has been shortcut.
|
||||||
pass
|
pass
|
||||||
else: # No match found.
|
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
|
# Check whether an intermediate join is required between current_table
|
||||||
# and new_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
|
# If the field is a geometry field, then the WHERE clause will need to be obtained
|
||||||
# with the get_geo_where_clause()
|
# with the get_geo_where_clause()
|
||||||
if hasattr(field, '_geom'):
|
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:
|
else:
|
||||||
where.append(get_where_clause(lookup_type, current_table + '.', column, value))
|
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
|
return joins, where, params
|
||||||
|
@ -24,7 +24,7 @@ class GeometryProxy(object):
|
|||||||
def __set__(self, obj, value):
|
def __set__(self, obj, value):
|
||||||
if isinstance(value, GEOSGeometry):
|
if isinstance(value, GEOSGeometry):
|
||||||
if value and ((value.srid is None) and (self._field._srid is not None)):
|
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
|
obj.__dict__[self._field.attname] = value
|
||||||
return value
|
return value
|
||||||
|
Loading…
x
Reference in New Issue
Block a user