diff --git a/django/contrib/gis/db/models/query.py b/django/contrib/gis/db/models/query.py index 66c45aa2be..07d593de02 100644 --- a/django/contrib/gis/db/models/query.py +++ b/django/contrib/gis/db/models/query.py @@ -1,6 +1,5 @@ import warnings -from django.contrib.gis.db.models import aggregates from django.contrib.gis.db.models.fields import ( GeometryField, LineStringField, PointField, get_srid_info, ) @@ -15,9 +14,7 @@ from django.db.models.expressions import RawSQL from django.db.models.fields import Field from django.db.models.query import QuerySet from django.utils import six -from django.utils.deprecation import ( - RemovedInDjango20Warning, RemovedInDjango110Warning, -) +from django.utils.deprecation import RemovedInDjango20Warning class GeoQuerySet(QuerySet): @@ -62,19 +59,6 @@ class GeoQuerySet(QuerySet): """ return self._geom_attribute('centroid', **kwargs) - def collect(self, **kwargs): - """ - Performs an aggregate collect operation on the given geometry field. - This is analogous to a union operation, but much faster because - boundaries are not dissolved. - """ - warnings.warn( - "The collect GeoQuerySet method is deprecated. Use the Collect() " - "aggregate in an aggregate() or annotate() method.", - RemovedInDjango110Warning, stacklevel=2 - ) - return self._spatial_aggregate(aggregates.Collect, **kwargs) - def difference(self, geom, **kwargs): """ Returns the spatial difference of the geographic field in a `difference` @@ -108,31 +92,6 @@ class GeoQuerySet(QuerySet): """ return self._geom_attribute('envelope', **kwargs) - def extent(self, **kwargs): - """ - Returns the extent (aggregate) of the features in the GeoQuerySet. The - extent will be returned as a 4-tuple, consisting of (xmin, ymin, xmax, ymax). - """ - warnings.warn( - "The extent GeoQuerySet method is deprecated. Use the Extent() " - "aggregate in an aggregate() or annotate() method.", - RemovedInDjango110Warning, stacklevel=2 - ) - return self._spatial_aggregate(aggregates.Extent, **kwargs) - - def extent3d(self, **kwargs): - """ - Returns the aggregate extent, in 3D, of the features in the - GeoQuerySet. It is returned as a 6-tuple, comprising: - (xmin, ymin, zmin, xmax, ymax, zmax). - """ - warnings.warn( - "The extent3d GeoQuerySet method is deprecated. Use the Extent3D() " - "aggregate in an aggregate() or annotate() method.", - RemovedInDjango110Warning, stacklevel=2 - ) - return self._spatial_aggregate(aggregates.Extent3D, **kwargs) - def force_rhr(self, **kwargs): """ Returns a modified version of the Polygon/MultiPolygon in which @@ -227,19 +186,6 @@ class GeoQuerySet(QuerySet): """ return self._distance_attribute('length', None, **kwargs) - def make_line(self, **kwargs): - """ - Creates a linestring from all of the PointField geometries in the - this GeoQuerySet and returns it. This is a spatial aggregate - method, and thus returns a geometry rather than a GeoQuerySet. - """ - warnings.warn( - "The make_line GeoQuerySet method is deprecated. Use the MakeLine() " - "aggregate in an aggregate() or annotate() method.", - RemovedInDjango110Warning, stacklevel=2 - ) - return self._spatial_aggregate(aggregates.MakeLine, geo_field_type=PointField, **kwargs) - def mem_size(self, **kwargs): """ Returns the memory size (number of bytes) that the geometry field takes @@ -415,19 +361,6 @@ class GeoQuerySet(QuerySet): """ return self._geomset_attribute('union', geom, **kwargs) - def unionagg(self, **kwargs): - """ - Performs an aggregate union on the given geometry field. Returns - None if the GeoQuerySet is empty. The `tolerance` keyword is for - Oracle backends only. - """ - warnings.warn( - "The unionagg GeoQuerySet method is deprecated. Use the Union() " - "aggregate in an aggregate() or annotate() method.", - RemovedInDjango110Warning, stacklevel=2 - ) - return self._spatial_aggregate(aggregates.Union, **kwargs) - # ### Private API -- Abstracted DRY routines. ### def _spatial_setup(self, att, desc=None, field_name=None, geo_field_type=None): """ @@ -462,35 +395,6 @@ class GeoQuerySet(QuerySet): return procedure_args, geo_field - def _spatial_aggregate(self, aggregate, field_name=None, - geo_field_type=None, tolerance=0.05): - """ - DRY routine for calling aggregate spatial stored procedures and - returning their result to the caller of the function. - """ - # Getting the field the geographic aggregate will be called on. - geo_field = self._geo_field(field_name) - if not geo_field: - raise TypeError('%s aggregate only available on GeometryFields.' % aggregate.name) - - # Checking if there are any geo field type limitations on this - # aggregate (e.g. ST_Makeline only operates on PointFields). - if geo_field_type is not None and not isinstance(geo_field, geo_field_type): - raise TypeError('%s aggregate may only be called on %ss.' % (aggregate.name, geo_field_type.__name__)) - - # Getting the string expression of the field name, as this is the - # argument taken by `Aggregate` objects. - agg_col = field_name or geo_field.name - - # Adding any keyword parameters for the Aggregate object. Oracle backends - # in particular need an additional `tolerance` parameter. - agg_kwargs = {} - if connections[self.db].ops.oracle: - agg_kwargs['tolerance'] = tolerance - - # Calling the QuerySet.aggregate, and returning only the value of the aggregate. - return self.aggregate(geoagg=aggregate(agg_col, **agg_kwargs))['geoagg'] - def _spatial_attribute(self, att, settings, field_name=None, model_att=None): """ DRY routine for calling a spatial stored procedure on a geometry column diff --git a/docs/ref/contrib/gis/geoquerysets.txt b/docs/ref/contrib/gis/geoquerysets.txt index d4b83e86c8..9a42bd7a15 100644 --- a/docs/ref/contrib/gis/geoquerysets.txt +++ b/docs/ref/contrib/gis/geoquerysets.txt @@ -1216,72 +1216,6 @@ Returns the number of points in the first linestring in the geometry field in a ``num_points`` attribute on each element of the ``GeoQuerySet``; otherwise sets with ``None``. -Spatial Aggregates -================== - -Aggregate Methods ------------------ - -.. deprecated:: 1.8 - - Aggregate methods are now deprecated. Prefer using their function-based - equivalents. - -``collect`` -~~~~~~~~~~~ - -.. method:: GeoQuerySet.collect(**kwargs) - -.. deprecated:: 1.8 - - Use the :class:`Collect` aggregate instead. - -Shortcut for ``aggregate(Collect())``. - -``extent`` -~~~~~~~~~~ - -.. method:: GeoQuerySet.extent(**kwargs) - -.. deprecated:: 1.8 - - Use the :class:`Extent` aggregate instead. - -Shortcut for ``aggregate(Extent())``. - -``extent3d`` -~~~~~~~~~~~~ - -.. method:: GeoQuerySet.extent3d(**kwargs) - -.. deprecated:: 1.8 - - Use the :class:`Extent` aggregate instead. - -Shortcut for ``aggregate(Extent3D())``. - -``make_line`` -~~~~~~~~~~~~~ - -.. method:: GeoQuerySet.make_line(**kwargs) - -.. deprecated:: 1.8 - - Use the :class:`MakeLine` aggregate instead. - -Shortcut for ``aggregate(MakeLine())``. - -``unionagg`` -~~~~~~~~~~~~ - -.. method:: GeoQuerySet.unionagg(**kwargs) - -.. deprecated:: 1.8 - - Use the :class:`Union` aggregate instead. - -Shortcut for ``aggregate(Union())``. - Aggregate Functions ------------------- diff --git a/docs/releases/1.2.txt b/docs/releases/1.2.txt index 368c436b39..1e36d4888b 100644 --- a/docs/releases/1.2.txt +++ b/docs/releases/1.2.txt @@ -358,8 +358,7 @@ Support for 3D geometry fields was added, and may be enabled by setting the :attr:`~django.contrib.gis.db.models.GeometryField.dim` keyword to 3 in your :class:`~django.contrib.gis.db.models.GeometryField`. The :class:`~django.contrib.gis.db.models.Extent3D` aggregate -and :meth:`~django.contrib.gis.db.models.GeoQuerySet.extent3d` ``GeoQuerySet`` -method were added as a part of this feature. +and ``extent3d()`` ``GeoQuerySet`` method were added as a part of this feature. The following :class:`~django.contrib.gis.db.models.GeoQuerySet` methods are new in 1.2: diff --git a/tests/gis_tests/geo3d/tests.py b/tests/gis_tests/geo3d/tests.py index 31b440be4a..156b59da09 100644 --- a/tests/gis_tests/geo3d/tests.py +++ b/tests/gis_tests/geo3d/tests.py @@ -12,9 +12,7 @@ from django.contrib.gis.gdal import HAS_GDAL from django.contrib.gis.geos import GEOSGeometry, LineString, Point, Polygon from django.test import TestCase, ignore_warnings, skipUnlessDBFeature from django.utils._os import upath -from django.utils.deprecation import ( - RemovedInDjango20Warning, RemovedInDjango110Warning, -) +from django.utils.deprecation import RemovedInDjango20Warning from .models import ( City3D, Interstate2D, Interstate3D, InterstateProj2D, InterstateProj3D, @@ -217,7 +215,6 @@ class Geo3DTest(Geo3DLoadingHelper, TestCase): self.assertSetEqual({p.ewkt for p in ref_union}, {p.ewkt for p in union}) @skipUnlessDBFeature("supports_3d_functions") - @ignore_warnings(category=RemovedInDjango110Warning) def test_extent(self): """ Testing the Extent3D aggregate for 3D models. @@ -225,16 +222,13 @@ class Geo3DTest(Geo3DLoadingHelper, TestCase): self._load_city_data() # `SELECT ST_Extent3D(point) FROM geo3d_city3d;` ref_extent3d = (-123.305196, -41.315268, 14, 174.783117, 48.462611, 1433) - extent1 = City3D.objects.aggregate(Extent3D('point'))['point__extent3d'] - extent2 = City3D.objects.extent3d() + extent = City3D.objects.aggregate(Extent3D('point'))['point__extent3d'] def check_extent3d(extent3d, tol=6): for ref_val, ext_val in zip(ref_extent3d, extent3d): self.assertAlmostEqual(ref_val, ext_val, tol) - for e3d in [extent1, extent2]: - check_extent3d(e3d) - self.assertIsNone(City3D.objects.none().extent3d()) + check_extent3d(extent) self.assertIsNone(City3D.objects.none().aggregate(Extent3D('point'))['point__extent3d']) @ignore_warnings(category=RemovedInDjango20Warning) diff --git a/tests/gis_tests/geoapp/feeds.py b/tests/gis_tests/geoapp/feeds.py index f2ebc46109..57d2d74866 100644 --- a/tests/gis_tests/geoapp/feeds.py +++ b/tests/gis_tests/geoapp/feeds.py @@ -23,8 +23,8 @@ class TestGeoRSS2(TestGeoRSS1): def geometry(self, obj): # This should attach a element for the extent of # of the cities in the database. This tuple came from - # calling `City.objects.extent()` -- we can't do that call here - # because `extent` is not implemented for MySQL/Oracle. + # calling `City.objects.aggregate(Extent())` -- we can't do that call + # here because `Extent` is not implemented for MySQL/Oracle. return (-123.30, -41.32, 174.78, 48.46) def item_geometry(self, item): diff --git a/tests/gis_tests/geoapp/tests.py b/tests/gis_tests/geoapp/tests.py index 0d6ee0362f..0dbf3fa5c5 100644 --- a/tests/gis_tests/geoapp/tests.py +++ b/tests/gis_tests/geoapp/tests.py @@ -13,9 +13,7 @@ from django.core.management import call_command from django.db import connection from django.test import TestCase, ignore_warnings, skipUnlessDBFeature from django.utils import six -from django.utils.deprecation import ( - RemovedInDjango20Warning, RemovedInDjango110Warning, -) +from django.utils.deprecation import RemovedInDjango20Warning from ..utils import no_oracle, oracle, postgis, spatialite from .models import ( @@ -493,11 +491,9 @@ class GeoQuerySetTest(TestCase): self.assertIsInstance(country.envelope, Polygon) @skipUnlessDBFeature("supports_extent_aggr") - @ignore_warnings(category=RemovedInDjango110Warning) def test_extent(self): """ - Testing the (deprecated) `extent` GeoQuerySet method and the Extent - aggregate. + Testing the `Extent` aggregate. """ # Reference query: # `SELECT ST_extent(point) FROM geoapp_city WHERE (name='Houston' or name='Dallas');` @@ -505,13 +501,9 @@ class GeoQuerySetTest(TestCase): expected = (-96.8016128540039, 29.7633724212646, -95.3631439208984, 32.782058715820) qs = City.objects.filter(name__in=('Houston', 'Dallas')) - extent1 = qs.extent() - extent2 = qs.aggregate(Extent('point'))['point__extent'] - - for extent in (extent1, extent2): - for val, exp in zip(extent, expected): - self.assertAlmostEqual(exp, val, 4) - self.assertIsNone(City.objects.filter(name=('Smalltown')).extent()) + extent = qs.aggregate(Extent('point'))['point__extent'] + for val, exp in zip(extent, expected): + self.assertAlmostEqual(exp, val, 4) self.assertIsNone(City.objects.filter(name=('Smalltown')).aggregate(Extent('point'))['point__extent']) @skipUnlessDBFeature("supports_extent_aggr") @@ -651,11 +643,9 @@ class GeoQuerySetTest(TestCase): for ptown in [ptown1, ptown2]: self.assertEqual('-104.609252,38.255001', ptown.kml) - @ignore_warnings(category=RemovedInDjango110Warning) def test_make_line(self): """ - Testing the (deprecated) `make_line` GeoQuerySet method and the MakeLine - aggregate. + Testing the `MakeLine` aggregate. """ if not connection.features.supports_make_line_aggr: # Only PostGIS has support for the MakeLine aggregate. For other @@ -666,9 +656,6 @@ class GeoQuerySetTest(TestCase): ) return - # Ensuring that a `TypeError` is raised on models without PointFields. - self.assertRaises(TypeError, State.objects.make_line) - self.assertRaises(TypeError, Country.objects.make_line) # MakeLine on an inappropriate field returns simply None self.assertIsNone(State.objects.aggregate(MakeLine('poly'))['poly__makeline']) # Reference query: @@ -681,11 +668,11 @@ class GeoQuerySetTest(TestCase): ) # We check for equality with a tolerance of 10e-5 which is a lower bound # of the precisions of ref_line coordinates - line1 = City.objects.make_line() - line2 = City.objects.aggregate(MakeLine('point'))['point__makeline'] - for line in (line1, line2): - self.assertTrue(ref_line.equals_exact(line, tolerance=10e-5), - "%s != %s" % (ref_line, line)) + line = City.objects.aggregate(MakeLine('point'))['point__makeline'] + self.assertTrue( + ref_line.equals_exact(line, tolerance=10e-5), + "%s != %s" % (ref_line, line) + ) @skipUnlessDBFeature("has_num_geom_method") def test_num_geom(self): @@ -863,33 +850,25 @@ class GeoQuerySetTest(TestCase): # but this seems unexpected and should be investigated to determine the cause. @skipUnlessDBFeature("has_unionagg_method") @no_oracle - @ignore_warnings(category=RemovedInDjango110Warning) def test_unionagg(self): """ - Testing the (deprecated) `unionagg` (aggregate union) GeoQuerySet method - and the Union aggregate. + Testing the `Union` aggregate. """ tx = Country.objects.get(name='Texas').mpoly # Houston, Dallas -- Ordering may differ depending on backend or GEOS version. union1 = fromstr('MULTIPOINT(-96.801611 32.782057,-95.363151 29.763374)') union2 = fromstr('MULTIPOINT(-95.363151 29.763374,-96.801611 32.782057)') qs = City.objects.filter(point__within=tx) - self.assertRaises(TypeError, qs.unionagg, 'name') self.assertRaises(ValueError, qs.aggregate, Union('name')) # Using `field_name` keyword argument in one query and specifying an # order in the other (which should not be used because this is # an aggregate method on a spatial column) - u1 = qs.unionagg(field_name='point') - u2 = qs.order_by('name').unionagg() - u3 = qs.aggregate(Union('point'))['point__union'] - u4 = qs.order_by('name').aggregate(Union('point'))['point__union'] + u1 = qs.aggregate(Union('point'))['point__union'] + u2 = qs.order_by('name').aggregate(Union('point'))['point__union'] tol = 0.00001 self.assertTrue(union1.equals_exact(u1, tol) or union2.equals_exact(u1, tol)) self.assertTrue(union1.equals_exact(u2, tol) or union2.equals_exact(u2, tol)) - self.assertTrue(union1.equals_exact(u3, tol) or union2.equals_exact(u3, tol)) - self.assertTrue(union1.equals_exact(u4, tol) or union2.equals_exact(u4, tol)) qs = City.objects.filter(name='NotACity') - self.assertIsNone(qs.unionagg(field_name='point')) self.assertIsNone(qs.aggregate(Union('point'))['point__union']) def test_within_subquery(self): diff --git a/tests/gis_tests/relatedapp/tests.py b/tests/gis_tests/relatedapp/tests.py index 19b0f89eab..14828455fb 100644 --- a/tests/gis_tests/relatedapp/tests.py +++ b/tests/gis_tests/relatedapp/tests.py @@ -4,10 +4,9 @@ from django.contrib.gis.db.models import F, Collect, Count, Extent, Union from django.contrib.gis.geometry.backend import Geometry from django.contrib.gis.geos import GEOSGeometry, MultiPoint, Point from django.db import connection -from django.test import TestCase, ignore_warnings, skipUnlessDBFeature +from django.test import TestCase, skipUnlessDBFeature from django.test.utils import override_settings from django.utils import timezone -from django.utils.deprecation import RemovedInDjango110Warning from ..utils import no_oracle from .models import ( @@ -64,17 +63,16 @@ class RelatedGeoModelTest(TestCase): check_pnt(GEOSGeometry(wkt, srid), qs[0].location.point) @skipUnlessDBFeature("supports_extent_aggr") - @ignore_warnings(category=RemovedInDjango110Warning) def test_related_extent_aggregate(self): - "Testing the `extent` GeoQuerySet aggregates on related geographic models." + "Testing the `Extent` aggregate on related geographic models." # This combines the Extent and Union aggregates into one query aggs = City.objects.aggregate(Extent('location__point')) # One for all locations, one that excludes New Mexico (Roswell). all_extent = (-104.528056, 29.763374, -79.460734, 40.18476) txpa_extent = (-97.516111, 29.763374, -79.460734, 40.18476) - e1 = City.objects.extent(field_name='location__point') - e2 = City.objects.exclude(state='NM').extent(field_name='location__point') + e1 = City.objects.aggregate(Extent('location__point'))['location__point__extent'] + e2 = City.objects.exclude(state='NM').aggregate(Extent('location__point'))['location__point__extent'] e3 = aggs['location__point__extent'] # The tolerance value is to four decimal places because of differences @@ -98,9 +96,8 @@ class RelatedGeoModelTest(TestCase): ) @skipUnlessDBFeature("has_unionagg_method") - @ignore_warnings(category=RemovedInDjango110Warning) def test_related_union_aggregate(self): - "Testing the `unionagg` GeoQuerySet aggregates on related geographic models." + "Testing the `Union` aggregate on related geographic models." # This combines the Extent and Union aggregates into one query aggs = City.objects.aggregate(Union('location__point')) @@ -114,14 +111,14 @@ class RelatedGeoModelTest(TestCase): # The second union aggregate is for a union # query that includes limiting information in the WHERE clause (in other - # words a `.filter()` precedes the call to `.unionagg()`). + # words a `.filter()` precedes the call to `.aggregate(Union()`). ref_u1 = MultiPoint(p1, p2, p4, p5, p3, srid=4326) ref_u2 = MultiPoint(p2, p3, srid=4326) - u1 = City.objects.unionagg(field_name='location__point') + u1 = City.objects.aggregate(Union('location__point'))['location__point__union'] u2 = City.objects.exclude( name__in=('Roswell', 'Houston', 'Dallas', 'Fort Worth'), - ).unionagg(field_name='location__point') + ).aggregate(Union('location__point'))['location__point__union'] u3 = aggs['location__point__union'] self.assertEqual(type(u1), MultiPoint) self.assertEqual(type(u3), MultiPoint) @@ -291,11 +288,9 @@ class RelatedGeoModelTest(TestCase): self.assertIsNone(b.author) @skipUnlessDBFeature("supports_collect_aggr") - @ignore_warnings(category=RemovedInDjango110Warning) def test_collect(self): """ - Testing the (deprecated) `collect` GeoQuerySet method and `Collect` - aggregate. + Testing the `Collect` aggregate. """ # Reference query: # SELECT AsText(ST_Collect("relatedapp_location"."point")) FROM "relatedapp_city" LEFT OUTER JOIN @@ -306,14 +301,11 @@ class RelatedGeoModelTest(TestCase): '-95.363151 29.763374,-96.801611 32.782057)' ) - c1 = City.objects.filter(state='TX').collect(field_name='location__point') - c2 = City.objects.filter(state='TX').aggregate(Collect('location__point'))['location__point__collect'] - - for coll in (c1, c2): - # Even though Dallas and Ft. Worth share same point, Collect doesn't - # consolidate -- that's why 4 points in MultiPoint. - self.assertEqual(4, len(coll)) - self.assertTrue(ref_geom.equals(coll)) + coll = City.objects.filter(state='TX').aggregate(Collect('location__point'))['location__point__collect'] + # Even though Dallas and Ft. Worth share same point, Collect doesn't + # consolidate -- that's why 4 points in MultiPoint. + self.assertEqual(4, len(coll)) + self.assertTrue(ref_geom.equals(coll)) def test15_invalid_select_related(self): "Testing doing select_related on the related name manager of a unique FK. See #13934."