From 6b659270d6813029c7d482bec68c623525471f0b Mon Sep 17 00:00:00 2001 From: Justin Bronn Date: Mon, 18 Jan 2010 21:49:13 +0000 Subject: [PATCH] Fixed problems associated with the `GeometryField` SRID cache: it is now keyed by the connection alias rather than the spatial backend name (e.g., `spatial_ref_sys` tables may have different entries, even if backend is the same), explicitly pass in connection alias to `SpatialRefSys` query to ensure correct entry is retrieved, and removed unused code from `GeometryField.get_distance`. Thanks, Josh Livni, for the bug report. git-svn-id: http://code.djangoproject.com/svn/django/trunk@12258 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- django/contrib/gis/db/backends/base.py | 7 +++ django/contrib/gis/db/models/fields.py | 68 ++++++++------------------ 2 files changed, 27 insertions(+), 48 deletions(-) diff --git a/django/contrib/gis/db/backends/base.py b/django/contrib/gis/db/backends/base.py index b977278b16..61c89fbf2b 100644 --- a/django/contrib/gis/db/backends/base.py +++ b/django/contrib/gis/db/backends/base.py @@ -122,6 +122,13 @@ class BaseSpatialOperations(object): def spatial_lookup_sql(self, lvalue, lookup_type, value, field): raise NotImplmentedError + # Routines for getting the OGC-compliant models. + def geometry_columns(self): + raise NotImplementedError + + def spatial_ref_sys(self): + raise NotImplementedError + class SpatialRefSysMixin(object): """ The SpatialRefSysMixin is a class used by the database-dependent diff --git a/django/contrib/gis/db/models/fields.py b/django/contrib/gis/db/models/fields.py index 65eb1f171c..0a78ce2292 100644 --- a/django/contrib/gis/db/models/fields.py +++ b/django/contrib/gis/db/models/fields.py @@ -1,45 +1,42 @@ from django.db.models.fields import Field +from django.db.models.sql.expressions import SQLEvaluator from django.utils.translation import ugettext_lazy as _ from django.contrib.gis import forms from django.contrib.gis.db.models.proxy import GeometryProxy from django.contrib.gis.geometry.backend import Geometry, GeometryException -from django.contrib.gis.measure import Distance -from django.db.models.sql.expressions import SQLEvaluator -# Local cache of the spatial_ref_sys table, which holds static data. -# This exists so that we don't have to hit the database each time -# we construct a distance query. -_srid_cache = {'postgis' : {}, - 'oracle' : {}, - 'spatialite' : {}, - } +# Local cache of the spatial_ref_sys table, which holds SRID data for each +# spatial database alias. This cache exists so that the database isn't queried +# for SRID info each time a distance query is constructed. +_srid_cache = {} def get_srid_info(srid, connection): """ Returns the units, unit name, and spheroid WKT associated with the given SRID from the `spatial_ref_sys` (or equivalent) spatial database - table. These results are cached. + table for the given database connection. These results are cached. """ global _srid_cache - # No `spatial_ref_sys` table in MySQL. - if connection.ops.mysql: + try: + # The SpatialRefSys model for the spatial backend. + SpatialRefSys = connection.ops.spatial_ref_sys() + except NotImplementedError: + # No `spatial_ref_sys` table in spatial backend (e.g., MySQL). return None, None, None - name = connection.ops.name - if not srid in _srid_cache[name]: - if connection.ops.postgis: - from django.contrib.gis.db.backends.postgis.models import SpatialRefSys - elif connection.ops.oracle: - from django.contrib.gis.db.backends.oracle.models import SpatialRefSys - elif connection.ops.spatialite: - from django.contrib.gis.db.backends.spatialite.models import SpatialRefSys - sr = SpatialRefSys.objects.get(srid=srid) + if not connection.alias in _srid_cache: + # Initialize SRID dictionary for database if it doesn't exist. + _srid_cache[connection.alias] = {} + + if not srid in _srid_cache[connection.alias]: + # Use `SpatialRefSys` model to query for spatial reference info. + sr = SpatialRefSys.objects.using(connection.alias).get(srid=srid) units, units_name = sr.units spheroid = SpatialRefSys.get_spheroid(sr.wkt) - _srid_cache[name][srid] = (units, units_name, spheroid) + _srid_cache[connection.alias][srid] = (units, units_name, spheroid) - return _srid_cache[name][srid] + return _srid_cache[connection.alias][srid] class GeometryField(Field): "The base GIS field -- maps to the OpenGIS Specification Geometry type." @@ -141,31 +138,6 @@ class GeometryField(Field): """ return connection.ops.get_distance(self, value, lookup_type) - if isinstance(dist, Distance): - if self.geodetic(connection): - # Won't allow Distance objects w/DWithin lookups on PostGIS. - if connection.ops.postgis and lookup_type == 'dwithin': - raise ValueError('Only numeric values of degree units are allowed on geographic DWithin queries.') - - # Spherical distance calculation parameter should be in meters. - dist_param = dist.m - else: - dist_param = getattr(dist, Distance.unit_attname(self.units_name(connection))) - else: - # Assuming the distance is in the units of the field. - dist_param = dist - - if connection.ops.oracle and lookup_type == 'dwithin': - dist_param = 'distance=%s' % dist_param - - if connection.ops.postgis and self.geodetic(connection) and lookup_type != 'dwithin' and option == 'spheroid': - # On PostGIS, by default `ST_distance_sphere` is used; but if the - # accuracy of `ST_distance_spheroid` is needed than the spheroid - # needs to be passed to the SQL stored procedure. - return [self._spheroid, dist_param] - else: - return [dist_param] - def get_prep_value(self, value): """ Spatial lookup values are either a parameter that is (or may be