1 from django.core.exceptions import ImproperlyConfigured |
1 from django.db import connections |
2 from django.db import connection |
|
3 from django.db.models.query import QuerySet, Q, ValuesQuerySet, ValuesListQuerySet |
2 from django.db.models.query import QuerySet, Q, ValuesQuerySet, ValuesListQuerySet |
4 |
3 |
5 from django.contrib.gis.db.backend import SpatialBackend |
|
6 from django.contrib.gis.db.models import aggregates |
4 from django.contrib.gis.db.models import aggregates |
7 from django.contrib.gis.db.models.fields import get_srid_info, GeometryField, PointField |
5 from django.contrib.gis.db.models.fields import get_srid_info, GeometryField, PointField, LineStringField |
8 from django.contrib.gis.db.models.sql import AreaField, DistanceField, GeomField, GeoQuery, GeoWhereNode |
6 from django.contrib.gis.db.models.sql import AreaField, DistanceField, GeomField, GeoQuery, GeoWhereNode |
|
7 from django.contrib.gis.geometry.backend import Geometry |
9 from django.contrib.gis.measure import Area, Distance |
8 from django.contrib.gis.measure import Area, Distance |
10 |
9 |
11 class GeoQuerySet(QuerySet): |
10 class GeoQuerySet(QuerySet): |
12 "The Geographic QuerySet." |
11 "The Geographic QuerySet." |
13 |
12 |
14 ### Methods overloaded from QuerySet ### |
13 ### Methods overloaded from QuerySet ### |
15 def __init__(self, model=None, query=None): |
14 def __init__(self, model=None, query=None, using=None): |
16 super(GeoQuerySet, self).__init__(model=model, query=query) |
15 super(GeoQuerySet, self).__init__(model=model, query=query, using=using) |
17 self.query = query or GeoQuery(self.model, connection) |
16 self.query = query or GeoQuery(self.model) |
18 |
17 |
19 def values(self, *fields): |
18 def values(self, *fields): |
20 return self._clone(klass=GeoValuesQuerySet, setup=True, _fields=fields) |
19 return self._clone(klass=GeoValuesQuerySet, setup=True, _fields=fields) |
21 |
20 |
22 def values_list(self, *fields, **kwargs): |
21 def values_list(self, *fields, **kwargs): |
108 Returns the extent (aggregate) of the features in the GeoQuerySet. The |
109 Returns the extent (aggregate) of the features in the GeoQuerySet. The |
109 extent will be returned as a 4-tuple, consisting of (xmin, ymin, xmax, ymax). |
110 extent will be returned as a 4-tuple, consisting of (xmin, ymin, xmax, ymax). |
110 """ |
111 """ |
111 return self._spatial_aggregate(aggregates.Extent, **kwargs) |
112 return self._spatial_aggregate(aggregates.Extent, **kwargs) |
112 |
113 |
|
114 def extent3d(self, **kwargs): |
|
115 """ |
|
116 Returns the aggregate extent, in 3D, of the features in the |
|
117 GeoQuerySet. It is returned as a 6-tuple, comprising: |
|
118 (xmin, ymin, zmin, xmax, ymax, zmax). |
|
119 """ |
|
120 return self._spatial_aggregate(aggregates.Extent3D, **kwargs) |
|
121 |
|
122 def force_rhr(self, **kwargs): |
|
123 """ |
|
124 Returns a modified version of the Polygon/MultiPolygon in which |
|
125 all of the vertices follow the Right-Hand-Rule. By default, |
|
126 this is attached as the `force_rhr` attribute on each element |
|
127 of the GeoQuerySet. |
|
128 """ |
|
129 return self._geom_attribute('force_rhr', **kwargs) |
|
130 |
113 def geojson(self, precision=8, crs=False, bbox=False, **kwargs): |
131 def geojson(self, precision=8, crs=False, bbox=False, **kwargs): |
114 """ |
132 """ |
115 Returns a GeoJSON representation of the geomtry field in a `geojson` |
133 Returns a GeoJSON representation of the geomtry field in a `geojson` |
116 attribute on each element of the GeoQuerySet. |
134 attribute on each element of the GeoQuerySet. |
117 |
135 |
118 The `crs` and `bbox` keywords may be set to True if the users wants |
136 The `crs` and `bbox` keywords may be set to True if the users wants |
119 the coordinate reference system and the bounding box to be included |
137 the coordinate reference system and the bounding box to be included |
120 in the GeoJSON representation of the geometry. |
138 in the GeoJSON representation of the geometry. |
121 """ |
139 """ |
122 if not SpatialBackend.postgis or not SpatialBackend.geojson: |
140 backend = connections[self.db].ops |
|
141 if not backend.geojson: |
123 raise NotImplementedError('Only PostGIS 1.3.4+ supports GeoJSON serialization.') |
142 raise NotImplementedError('Only PostGIS 1.3.4+ supports GeoJSON serialization.') |
124 |
143 |
125 if not isinstance(precision, (int, long)): |
144 if not isinstance(precision, (int, long)): |
126 raise TypeError('Precision keyword must be set with an integer.') |
145 raise TypeError('Precision keyword must be set with an integer.') |
127 |
146 |
128 # Setting the options flag -- which depends on which version of |
147 # Setting the options flag -- which depends on which version of |
129 # PostGIS we're using. |
148 # PostGIS we're using. |
130 major, minor1, minor2 = SpatialBackend.version |
149 if backend.spatial_version >= (1, 4, 0): |
131 if major >=1 and (minor1 >= 4): |
|
132 options = 0 |
150 options = 0 |
133 if crs and bbox: options = 3 |
151 if crs and bbox: options = 3 |
134 elif bbox: options = 1 |
152 elif bbox: options = 1 |
135 elif crs: options = 2 |
153 elif crs: options = 2 |
136 else: |
154 else: |
137 options = 0 |
155 options = 0 |
138 if crs and bbox: options = 3 |
156 if crs and bbox: options = 3 |
139 elif crs: options = 1 |
157 elif crs: options = 1 |
140 elif bbox: options = 2 |
158 elif bbox: options = 2 |
141 s = {'desc' : 'GeoJSON', |
159 s = {'desc' : 'GeoJSON', |
142 'procedure_args' : {'precision' : precision, 'options' : options}, |
160 'procedure_args' : {'precision' : precision, 'options' : options}, |
143 'procedure_fmt' : '%(geo_col)s,%(precision)s,%(options)s', |
161 'procedure_fmt' : '%(geo_col)s,%(precision)s,%(options)s', |
144 } |
162 } |
145 return self._spatial_attribute('geojson', s, **kwargs) |
163 return self._spatial_attribute('geojson', s, **kwargs) |
146 |
164 |
|
165 def geohash(self, precision=20, **kwargs): |
|
166 """ |
|
167 Returns a GeoHash representation of the given field in a `geohash` |
|
168 attribute on each element of the GeoQuerySet. |
|
169 |
|
170 The `precision` keyword may be used to custom the number of |
|
171 _characters_ used in the output GeoHash, the default is 20. |
|
172 """ |
|
173 s = {'desc' : 'GeoHash', |
|
174 'procedure_args': {'precision': precision}, |
|
175 'procedure_fmt': '%(geo_col)s,%(precision)s', |
|
176 } |
|
177 return self._spatial_attribute('geohash', s, **kwargs) |
|
178 |
147 def gml(self, precision=8, version=2, **kwargs): |
179 def gml(self, precision=8, version=2, **kwargs): |
148 """ |
180 """ |
149 Returns GML representation of the given field in a `gml` attribute |
181 Returns GML representation of the given field in a `gml` attribute |
150 on each element of the GeoQuerySet. |
182 on each element of the GeoQuerySet. |
151 """ |
183 """ |
|
184 backend = connections[self.db].ops |
152 s = {'desc' : 'GML', 'procedure_args' : {'precision' : precision}} |
185 s = {'desc' : 'GML', 'procedure_args' : {'precision' : precision}} |
153 if SpatialBackend.postgis: |
186 if backend.postgis: |
154 # PostGIS AsGML() aggregate function parameter order depends on the |
187 # PostGIS AsGML() aggregate function parameter order depends on the |
155 # version -- uggh. |
188 # version -- uggh. |
156 major, minor1, minor2 = SpatialBackend.version |
189 if backend.spatial_version > (1, 3, 1): |
157 if major >= 1 and (minor1 > 3 or (minor1 == 3 and minor2 > 1)): |
|
158 procedure_fmt = '%(version)s,%(geo_col)s,%(precision)s' |
190 procedure_fmt = '%(version)s,%(geo_col)s,%(precision)s' |
159 else: |
191 else: |
160 procedure_fmt = '%(geo_col)s,%(precision)s,%(version)s' |
192 procedure_fmt = '%(geo_col)s,%(precision)s,%(version)s' |
161 s['procedure_args'] = {'precision' : precision, 'version' : version} |
193 s['procedure_args'] = {'precision' : precision, 'version' : version} |
162 |
194 |
469 settings.setdefault('geom_field', None) |
516 settings.setdefault('geom_field', None) |
470 settings.setdefault('procedure_args', {}) |
517 settings.setdefault('procedure_args', {}) |
471 settings.setdefault('procedure_fmt', '%(geo_col)s') |
518 settings.setdefault('procedure_fmt', '%(geo_col)s') |
472 settings.setdefault('select_params', []) |
519 settings.setdefault('select_params', []) |
473 |
520 |
|
521 connection = connections[self.db] |
|
522 backend = connection.ops |
|
523 |
474 # Performing setup for the spatial column, unless told not to. |
524 # Performing setup for the spatial column, unless told not to. |
475 if settings.get('setup', True): |
525 if settings.get('setup', True): |
476 default_args, geo_field = self._spatial_setup(att, desc=settings['desc'], field_name=field_name) |
526 default_args, geo_field = self._spatial_setup(att, desc=settings['desc'], field_name=field_name, |
|
527 geo_field_type=settings.get('geo_field_type', None)) |
477 for k, v in default_args.iteritems(): settings['procedure_args'].setdefault(k, v) |
528 for k, v in default_args.iteritems(): settings['procedure_args'].setdefault(k, v) |
478 else: |
529 else: |
479 geo_field = settings['geo_field'] |
530 geo_field = settings['geo_field'] |
480 |
531 |
481 # The attribute to attach to the model. |
532 # The attribute to attach to the model. |
482 if not isinstance(model_att, basestring): model_att = att |
533 if not isinstance(model_att, basestring): model_att = att |
483 |
534 |
484 # Special handling for any argument that is a geometry. |
535 # Special handling for any argument that is a geometry. |
485 for name in settings['geom_args']: |
536 for name in settings['geom_args']: |
486 # Using the field's get_db_prep_lookup() to get any needed |
537 # Using the field's get_placeholder() routine to get any needed |
487 # transformation SQL -- we pass in a 'dummy' `contains` lookup. |
538 # transformation SQL. |
488 where, params = geo_field.get_db_prep_lookup('contains', settings['procedure_args'][name]) |
539 geom = geo_field.get_prep_value(settings['procedure_args'][name]) |
|
540 params = geo_field.get_db_prep_lookup('contains', geom, connection=connection) |
|
541 geom_placeholder = geo_field.get_placeholder(geom, connection) |
|
542 |
489 # Replacing the procedure format with that of any needed |
543 # Replacing the procedure format with that of any needed |
490 # transformation SQL. |
544 # transformation SQL. |
491 old_fmt = '%%(%s)s' % name |
545 old_fmt = '%%(%s)s' % name |
492 new_fmt = where[0] % '%%s' |
546 new_fmt = geom_placeholder % '%%s' |
493 settings['procedure_fmt'] = settings['procedure_fmt'].replace(old_fmt, new_fmt) |
547 settings['procedure_fmt'] = settings['procedure_fmt'].replace(old_fmt, new_fmt) |
494 settings['select_params'].extend(params) |
548 settings['select_params'].extend(params) |
495 |
549 |
496 # Getting the format for the stored procedure. |
550 # Getting the format for the stored procedure. |
497 fmt = '%%(function)s(%s)' % settings['procedure_fmt'] |
551 fmt = '%%(function)s(%s)' % settings['procedure_fmt'] |
498 |
552 |
499 # If the result of this function needs to be converted. |
553 # If the result of this function needs to be converted. |
500 if settings.get('select_field', False): |
554 if settings.get('select_field', False): |
501 sel_fld = settings['select_field'] |
555 sel_fld = settings['select_field'] |
502 if isinstance(sel_fld, GeomField) and SpatialBackend.select: |
556 if isinstance(sel_fld, GeomField) and backend.select: |
503 self.query.custom_select[model_att] = SpatialBackend.select |
557 self.query.custom_select[model_att] = backend.select |
|
558 if connection.ops.oracle: |
|
559 sel_fld.empty_strings_allowed = False |
504 self.query.extra_select_fields[model_att] = sel_fld |
560 self.query.extra_select_fields[model_att] = sel_fld |
505 |
561 |
506 # Finally, setting the extra selection attribute with |
562 # Finally, setting the extra selection attribute with |
507 # the format string expanded with the stored procedure |
563 # the format string expanded with the stored procedure |
508 # arguments. |
564 # arguments. |
517 procedure_args, geo_field = self._spatial_setup(func, field_name=kwargs.get('field_name', None)) |
573 procedure_args, geo_field = self._spatial_setup(func, field_name=kwargs.get('field_name', None)) |
518 |
574 |
519 # If geodetic defaulting distance attribute to meters (Oracle and |
575 # If geodetic defaulting distance attribute to meters (Oracle and |
520 # PostGIS spherical distances return meters). Otherwise, use the |
576 # PostGIS spherical distances return meters). Otherwise, use the |
521 # units of the geometry field. |
577 # units of the geometry field. |
522 if geo_field.geodetic: |
578 connection = connections[self.db] |
|
579 geodetic = geo_field.geodetic(connection) |
|
580 geography = geo_field.geography |
|
581 |
|
582 if geodetic: |
523 dist_att = 'm' |
583 dist_att = 'm' |
524 else: |
584 else: |
525 dist_att = Distance.unit_attname(geo_field.units_name) |
585 dist_att = Distance.unit_attname(geo_field.units_name(connection)) |
526 |
586 |
527 # Shortcut booleans for what distance function we're using. |
587 # Shortcut booleans for what distance function we're using and |
|
588 # whether the geometry field is 3D. |
528 distance = func == 'distance' |
589 distance = func == 'distance' |
529 length = func == 'length' |
590 length = func == 'length' |
530 perimeter = func == 'perimeter' |
591 perimeter = func == 'perimeter' |
531 if not (distance or length or perimeter): |
592 if not (distance or length or perimeter): |
532 raise ValueError('Unknown distance function: %s' % func) |
593 raise ValueError('Unknown distance function: %s' % func) |
|
594 geom_3d = geo_field.dim == 3 |
533 |
595 |
534 # The field's get_db_prep_lookup() is used to get any |
596 # The field's get_db_prep_lookup() is used to get any |
535 # extra distance parameters. Here we set up the |
597 # extra distance parameters. Here we set up the |
536 # parameters that will be passed in to field's function. |
598 # parameters that will be passed in to field's function. |
537 lookup_params = [geom or 'POINT (0 0)', 0] |
599 lookup_params = [geom or 'POINT (0 0)', 0] |
538 |
600 |
|
601 # Getting the spatial backend operations. |
|
602 backend = connection.ops |
|
603 |
539 # If the spheroid calculation is desired, either by the `spheroid` |
604 # If the spheroid calculation is desired, either by the `spheroid` |
540 # keyword or when calculating the length of geodetic field, make |
605 # keyword or when calculating the length of geodetic field, make |
541 # sure the 'spheroid' distance setting string is passed in so we |
606 # sure the 'spheroid' distance setting string is passed in so we |
542 # get the correct spatial stored procedure. |
607 # get the correct spatial stored procedure. |
543 if spheroid or (SpatialBackend.postgis and geo_field.geodetic and length): |
608 if spheroid or (backend.postgis and geodetic and |
|
609 (not geography) and length): |
544 lookup_params.append('spheroid') |
610 lookup_params.append('spheroid') |
545 where, params = geo_field.get_db_prep_lookup('distance_lte', lookup_params) |
611 lookup_params = geo_field.get_prep_value(lookup_params) |
|
612 params = geo_field.get_db_prep_lookup('distance_lte', lookup_params, connection=connection) |
546 |
613 |
547 # The `geom_args` flag is set to true if a geometry parameter was |
614 # The `geom_args` flag is set to true if a geometry parameter was |
548 # passed in. |
615 # passed in. |
549 geom_args = bool(geom) |
616 geom_args = bool(geom) |
550 |
617 |
551 if SpatialBackend.oracle: |
618 if backend.oracle: |
552 if distance: |
619 if distance: |
553 procedure_fmt = '%(geo_col)s,%(geom)s,%(tolerance)s' |
620 procedure_fmt = '%(geo_col)s,%(geom)s,%(tolerance)s' |
554 elif length or perimeter: |
621 elif length or perimeter: |
555 procedure_fmt = '%(geo_col)s,%(tolerance)s' |
622 procedure_fmt = '%(geo_col)s,%(tolerance)s' |
556 procedure_args['tolerance'] = tolerance |
623 procedure_args['tolerance'] = tolerance |
557 else: |
624 else: |
558 # Getting whether this field is in units of degrees since the field may have |
625 # Getting whether this field is in units of degrees since the field may have |
559 # been transformed via the `transform` GeoQuerySet method. |
626 # been transformed via the `transform` GeoQuerySet method. |
560 if self.query.transformed_srid: |
627 if self.query.transformed_srid: |
561 u, unit_name, s = get_srid_info(self.query.transformed_srid) |
628 u, unit_name, s = get_srid_info(self.query.transformed_srid, connection) |
562 geodetic = unit_name in geo_field.geodetic_units |
629 geodetic = unit_name in geo_field.geodetic_units |
563 else: |
630 |
564 geodetic = geo_field.geodetic |
631 if backend.spatialite and geodetic: |
565 |
|
566 if SpatialBackend.spatialite and geodetic: |
|
567 raise ValueError('SQLite does not support linear distance calculations on geodetic coordinate systems.') |
632 raise ValueError('SQLite does not support linear distance calculations on geodetic coordinate systems.') |
568 |
633 |
569 if distance: |
634 if distance: |
570 if self.query.transformed_srid: |
635 if self.query.transformed_srid: |
571 # Setting the `geom_args` flag to false because we want to handle |
636 # Setting the `geom_args` flag to false because we want to handle |
572 # transformation SQL here, rather than the way done by default |
637 # transformation SQL here, rather than the way done by default |
573 # (which will transform to the original SRID of the field rather |
638 # (which will transform to the original SRID of the field rather |
574 # than to what was transformed to). |
639 # than to what was transformed to). |
575 geom_args = False |
640 geom_args = False |
576 procedure_fmt = '%s(%%(geo_col)s, %s)' % (SpatialBackend.transform, self.query.transformed_srid) |
641 procedure_fmt = '%s(%%(geo_col)s, %s)' % (backend.transform, self.query.transformed_srid) |
577 if geom.srid is None or geom.srid == self.query.transformed_srid: |
642 if geom.srid is None or geom.srid == self.query.transformed_srid: |
578 # If the geom parameter srid is None, it is assumed the coordinates |
643 # If the geom parameter srid is None, it is assumed the coordinates |
579 # are in the transformed units. A placeholder is used for the |
644 # are in the transformed units. A placeholder is used for the |
580 # geometry parameter. `GeomFromText` constructor is also needed |
645 # geometry parameter. `GeomFromText` constructor is also needed |
581 # to wrap geom placeholder for SpatiaLite. |
646 # to wrap geom placeholder for SpatiaLite. |
582 if SpatialBackend.spatialite: |
647 if backend.spatialite: |
583 procedure_fmt += ', %s(%%%%s, %s)' % (SpatialBackend.from_text, self.query.transformed_srid) |
648 procedure_fmt += ', %s(%%%%s, %s)' % (backend.from_text, self.query.transformed_srid) |
584 else: |
649 else: |
585 procedure_fmt += ', %%s' |
650 procedure_fmt += ', %%s' |
586 else: |
651 else: |
587 # We need to transform the geom to the srid specified in `transform()`, |
652 # We need to transform the geom to the srid specified in `transform()`, |
588 # so wrapping the geometry placeholder in transformation SQL. |
653 # so wrapping the geometry placeholder in transformation SQL. |
589 # SpatiaLite also needs geometry placeholder wrapped in `GeomFromText` |
654 # SpatiaLite also needs geometry placeholder wrapped in `GeomFromText` |
590 # constructor. |
655 # constructor. |
591 if SpatialBackend.spatialite: |
656 if backend.spatialite: |
592 procedure_fmt += ', %s(%s(%%%%s, %s), %s)' % (SpatialBackend.transform, SpatialBackend.from_text, |
657 procedure_fmt += ', %s(%s(%%%%s, %s), %s)' % (backend.transform, backend.from_text, |
593 geom.srid, self.query.transformed_srid) |
658 geom.srid, self.query.transformed_srid) |
594 else: |
659 else: |
595 procedure_fmt += ', %s(%%%%s, %s)' % (SpatialBackend.transform, self.query.transformed_srid) |
660 procedure_fmt += ', %s(%%%%s, %s)' % (backend.transform, self.query.transformed_srid) |
596 else: |
661 else: |
597 # `transform()` was not used on this GeoQuerySet. |
662 # `transform()` was not used on this GeoQuerySet. |
598 procedure_fmt = '%(geo_col)s,%(geom)s' |
663 procedure_fmt = '%(geo_col)s,%(geom)s' |
599 |
664 |
600 if geodetic: |
665 if not geography and geodetic: |
601 # Spherical distance calculation is needed (because the geographic |
666 # Spherical distance calculation is needed (because the geographic |
602 # field is geodetic). However, the PostGIS ST_distance_sphere/spheroid() |
667 # field is geodetic). However, the PostGIS ST_distance_sphere/spheroid() |
603 # procedures may only do queries from point columns to point geometries |
668 # procedures may only do queries from point columns to point geometries |
604 # some error checking is required. |
669 # some error checking is required. |
605 if not isinstance(geo_field, PointField): |
670 if not backend.geography: |
606 raise ValueError('Spherical distance calculation only supported on PointFields.') |
671 if not isinstance(geo_field, PointField): |
607 if not str(SpatialBackend.Geometry(buffer(params[0].wkb)).geom_type) == 'Point': |
672 raise ValueError('Spherical distance calculation only supported on PointFields.') |
608 raise ValueError('Spherical distance calculation only supported with Point Geometry parameters') |
673 if not str(Geometry(buffer(params[0].ewkb)).geom_type) == 'Point': |
|
674 raise ValueError('Spherical distance calculation only supported with Point Geometry parameters') |
609 # The `function` procedure argument needs to be set differently for |
675 # The `function` procedure argument needs to be set differently for |
610 # geodetic distance calculations. |
676 # geodetic distance calculations. |
611 if spheroid: |
677 if spheroid: |
612 # Call to distance_spheroid() requires spheroid param as well. |
678 # Call to distance_spheroid() requires spheroid param as well. |
613 procedure_fmt += ',%(spheroid)s' |
679 procedure_fmt += ",'%(spheroid)s'" |
614 procedure_args.update({'function' : SpatialBackend.distance_spheroid, 'spheroid' : where[1]}) |
680 procedure_args.update({'function' : backend.distance_spheroid, 'spheroid' : params[1]}) |
615 else: |
681 else: |
616 procedure_args.update({'function' : SpatialBackend.distance_sphere}) |
682 procedure_args.update({'function' : backend.distance_sphere}) |
617 elif length or perimeter: |
683 elif length or perimeter: |
618 procedure_fmt = '%(geo_col)s' |
684 procedure_fmt = '%(geo_col)s' |
619 if geodetic and length: |
685 if not geography and geodetic and length: |
620 # There's no `length_sphere` |
686 # There's no `length_sphere`, and `length_spheroid` also |
621 procedure_fmt += ',%(spheroid)s' |
687 # works on 3D geometries. |
622 procedure_args.update({'function' : SpatialBackend.length_spheroid, 'spheroid' : where[1]}) |
688 procedure_fmt += ",'%(spheroid)s'" |
|
689 procedure_args.update({'function' : backend.length_spheroid, 'spheroid' : params[1]}) |
|
690 elif geom_3d and backend.postgis: |
|
691 # Use 3D variants of perimeter and length routines on PostGIS. |
|
692 if perimeter: |
|
693 procedure_args.update({'function' : backend.perimeter3d}) |
|
694 elif length: |
|
695 procedure_args.update({'function' : backend.length3d}) |
623 |
696 |
624 # Setting up the settings for `_spatial_attribute`. |
697 # Setting up the settings for `_spatial_attribute`. |
625 s = {'select_field' : DistanceField(dist_att), |
698 s = {'select_field' : DistanceField(dist_att), |
626 'setup' : False, |
699 'setup' : False, |
627 'geo_field' : geo_field, |
700 'geo_field' : geo_field, |
675 if not geo_field in opts.fields: |
748 if not geo_field in opts.fields: |
676 # Is this operation going to be on a related geographic field? |
749 # Is this operation going to be on a related geographic field? |
677 # If so, it'll have to be added to the select related information |
750 # If so, it'll have to be added to the select related information |
678 # (e.g., if 'location__point' was given as the field name). |
751 # (e.g., if 'location__point' was given as the field name). |
679 self.query.add_select_related([field_name]) |
752 self.query.add_select_related([field_name]) |
680 self.query.pre_sql_setup() |
753 compiler = self.query.get_compiler(self.db) |
|
754 compiler.pre_sql_setup() |
681 rel_table, rel_col = self.query.related_select_cols[self.query.related_select_fields.index(geo_field)] |
755 rel_table, rel_col = self.query.related_select_cols[self.query.related_select_fields.index(geo_field)] |
682 return self.query._field_column(geo_field, rel_table) |
756 return compiler._field_column(geo_field, rel_table) |
683 elif not geo_field in opts.local_fields: |
757 elif not geo_field in opts.local_fields: |
684 # This geographic field is inherited from another model, so we have to |
758 # This geographic field is inherited from another model, so we have to |
685 # use the db table for the _parent_ model instead. |
759 # use the db table for the _parent_ model instead. |
686 tmp_fld, parent_model, direct, m2m = opts.get_field_by_name(geo_field.name) |
760 tmp_fld, parent_model, direct, m2m = opts.get_field_by_name(geo_field.name) |
687 return self.query._field_column(geo_field, parent_model._meta.db_table) |
761 return self.query.get_compiler(self.db)._field_column(geo_field, parent_model._meta.db_table) |
688 else: |
762 else: |
689 return self.query._field_column(geo_field) |
763 return self.query.get_compiler(self.db)._field_column(geo_field) |
690 |
764 |
691 class GeoValuesQuerySet(ValuesQuerySet): |
765 class GeoValuesQuerySet(ValuesQuerySet): |
692 def __init__(self, *args, **kwargs): |
766 def __init__(self, *args, **kwargs): |
693 super(GeoValuesQuerySet, self).__init__(*args, **kwargs) |
767 super(GeoValuesQuerySet, self).__init__(*args, **kwargs) |
694 # This flag tells `resolve_columns` to run the values through |
768 # This flag tells `resolve_columns` to run the values through |