From ea27e26b0907e74ab8d6c46a9694775a310fdc67 Mon Sep 17 00:00:00 2001 From: Claude Paroz Date: Thu, 29 Jan 2015 20:12:08 +0100 Subject: [PATCH] Deprecated legacy GeoManager/GeoQuerySet methods --- django/contrib/gis/db/models/manager.py | 11 +++++++++++ django/contrib/gis/db/models/query.py | 9 ++++++++- django/contrib/gis/sitemaps/views.py | 11 +++++++---- docs/internals/deprecation.txt | 2 ++ docs/releases/1.9.txt | 10 ++++++++++ tests/gis_tests/distapp/tests.py | 11 ++++++++++- tests/gis_tests/geo3d/tests.py | 10 +++++++++- tests/gis_tests/geoapp/tests.py | 5 ++++- tests/gis_tests/geogapp/tests.py | 5 ++++- tests/runtests.py | 5 +++++ 10 files changed, 70 insertions(+), 9 deletions(-) diff --git a/django/contrib/gis/db/models/manager.py b/django/contrib/gis/db/models/manager.py index 22d009a972..91caaef171 100644 --- a/django/contrib/gis/db/models/manager.py +++ b/django/contrib/gis/db/models/manager.py @@ -1,5 +1,8 @@ +import warnings + from django.contrib.gis.db.models.query import GeoQuerySet from django.db.models.manager import Manager +from django.utils.deprecation import RemovedInDjango21Warning class GeoManager(Manager.from_queryset(GeoQuerySet)): @@ -9,3 +12,11 @@ class GeoManager(Manager.from_queryset(GeoQuerySet)): # so that geometry columns on Oracle and MySQL are selected # properly. use_for_related_fields = True + + def __init__(self, *args, **kwargs): + warnings.warn( + "The GeoManager class is deprecated. Simply use a normal manager " + "once you have replaced all calls to GeoQuerySet methods by annotations.", + RemovedInDjango21Warning, stacklevel=2 + ) + super(GeoManager, self).__init__(*args, **kwargs) diff --git a/django/contrib/gis/db/models/query.py b/django/contrib/gis/db/models/query.py index f287c778da..c2a46399d2 100644 --- a/django/contrib/gis/db/models/query.py +++ b/django/contrib/gis/db/models/query.py @@ -15,7 +15,9 @@ 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 +from django.utils.deprecation import ( + RemovedInDjango20Warning, RemovedInDjango21Warning, +) class GeoQuerySet(QuerySet): @@ -513,6 +515,11 @@ class GeoQuerySet(QuerySet): The name of the model attribute to attach the output of the spatial function to. """ + warnings.warn( + "The %s GeoQuerySet method is deprecated. See GeoDjango Functions " + "documentation to find the expression-based replacement." % att, + RemovedInDjango21Warning, stacklevel=2 + ) # Default settings. settings.setdefault('desc', None) settings.setdefault('geom_args', ()) diff --git a/django/contrib/gis/sitemaps/views.py b/django/contrib/gis/sitemaps/views.py index dc5eb40441..c5fd1aa904 100644 --- a/django/contrib/gis/sitemaps/views.py +++ b/django/contrib/gis/sitemaps/views.py @@ -2,6 +2,7 @@ from __future__ import unicode_literals from django.apps import apps from django.contrib.gis.db.models.fields import GeometryField +from django.contrib.gis.db.models.functions import AsKML, Transform from django.contrib.gis.shortcuts import render_to_kml, render_to_kmz from django.core.exceptions import FieldDoesNotExist from django.db import DEFAULT_DB_ALIAS, connections @@ -31,15 +32,17 @@ def kml(request, label, model, field_name=None, compress=False, using=DEFAULT_DB connection = connections[using] - if connection.features.has_kml_method: + if connection.features.has_AsKML_function: # Database will take care of transformation. - placemarks = klass._default_manager.using(using).kml(field_name=field_name) + placemarks = klass._default_manager.using(using).annotate(kml=AsKML(field_name)) else: # If the database offers no KML method, we use the `kml` # attribute of the lazy geometry instead. placemarks = [] - if connection.features.has_transform_method: - qs = klass._default_manager.using(using).transform(4326, field_name=field_name) + if connection.features.has_Transform_function: + qs = klass._default_manager.using(using).annotate( + **{'%s_4326' % field_name: Transform(field_name, 4326)}) + field_name += '_4326' else: qs = klass._default_manager.using(using).all() for mod in qs: diff --git a/docs/internals/deprecation.txt b/docs/internals/deprecation.txt index 026aa916c8..98432a7e38 100644 --- a/docs/internals/deprecation.txt +++ b/docs/internals/deprecation.txt @@ -35,6 +35,8 @@ details on these changes. * The ``django.contrib.auth.tests.utils.skipIfCustomUser()`` decorator will be removed. +* The ``GeoManager`` and ``GeoQuerySet`` classes will be removed. + .. _deprecation-removed-in-2.0: 2.0 diff --git a/docs/releases/1.9.txt b/docs/releases/1.9.txt index 0d0f3d5e01..dcf948c810 100644 --- a/docs/releases/1.9.txt +++ b/docs/releases/1.9.txt @@ -400,6 +400,16 @@ of its methods and attributes are either changed or renamed. The aim of these changes is to provide a documented API for relation fields. +``GeoManager`` and ``GeoQuerySet`` custom methods +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +All custom ``GeoQuerySet`` methods (``area()``, ``distance()``, ``gml()``, ...) +have been replaced by equivalent geographic expressions in annotations (see in +new features). Hence the need to set a custom ``GeoManager`` to GIS-enabled +models is now obsolete. As soon as your code doesn't call any of the deprecated +methods, you can simply remove the ``objects = GeoManager()`` lines from your +models. + Miscellaneous ~~~~~~~~~~~~~ diff --git a/tests/gis_tests/distapp/tests.py b/tests/gis_tests/distapp/tests.py index e6d66d22ae..077675f3ee 100644 --- a/tests/gis_tests/distapp/tests.py +++ b/tests/gis_tests/distapp/tests.py @@ -7,7 +7,8 @@ from django.contrib.gis.geos import HAS_GEOS from django.contrib.gis.measure import D # alias for Distance from django.db import connection from django.db.models import Q -from django.test import TestCase, skipUnlessDBFeature +from django.test import TestCase, ignore_warnings, skipUnlessDBFeature +from django.utils.deprecation import RemovedInDjango21Warning from ..utils import no_oracle, oracle, postgis, spatialite @@ -98,6 +99,7 @@ class DistanceTest(TestCase): self.assertListEqual(au_cities, self.get_names(qs.filter(point__dwithin=(self.au_pnt, dist)))) @skipUnlessDBFeature("has_distance_method") + @ignore_warnings(category=RemovedInDjango21Warning) def test_distance_projected(self): """ Test the `distance` GeoQuerySet method on projected coordinate systems. @@ -141,6 +143,7 @@ class DistanceTest(TestCase): self.assertAlmostEqual(ft_distances[i], c.distance.survey_ft, tol) @skipUnlessDBFeature("has_distance_method", "supports_distance_geodetic") + @ignore_warnings(category=RemovedInDjango21Warning) def test_distance_geodetic(self): """ Test the `distance` GeoQuerySet method on geodetic coordinate systems. @@ -202,6 +205,7 @@ class DistanceTest(TestCase): @no_oracle # Oracle already handles geographic distance calculation. @skipUnlessDBFeature("has_distance_method") + @ignore_warnings(category=RemovedInDjango21Warning) def test_distance_transform(self): """ Test the `distance` GeoQuerySet method used with `transform` on a geographic field. @@ -322,6 +326,7 @@ class DistanceTest(TestCase): self.assertEqual(cities, ['Adelaide', 'Hobart', 'Shellharbour', 'Thirroul']) @skipUnlessDBFeature("has_area_method") + @ignore_warnings(category=RemovedInDjango21Warning) def test_area(self): """ Test the `area` GeoQuerySet method. @@ -335,6 +340,7 @@ class DistanceTest(TestCase): self.assertAlmostEqual(area_sq_m[i], z.area.sq_m, tol) @skipUnlessDBFeature("has_length_method") + @ignore_warnings(category=RemovedInDjango21Warning) def test_length(self): """ Test the `length` GeoQuerySet method. @@ -358,6 +364,7 @@ class DistanceTest(TestCase): self.assertAlmostEqual(len_m2, i10.length.m, 2) @skipUnlessDBFeature("has_perimeter_method") + @ignore_warnings(category=RemovedInDjango21Warning) def test_perimeter(self): """ Test the `perimeter` GeoQuerySet method. @@ -374,6 +381,7 @@ class DistanceTest(TestCase): self.assertEqual(0, c.perim.m) @skipUnlessDBFeature("has_area_method", "has_distance_method") + @ignore_warnings(category=RemovedInDjango21Warning) def test_measurement_null_fields(self): """ Test the measurement GeoQuerySet methods on fields with NULL values. @@ -388,6 +396,7 @@ class DistanceTest(TestCase): self.assertIsNone(z.area) @skipUnlessDBFeature("has_distance_method") + @ignore_warnings(category=RemovedInDjango21Warning) def test_distance_order_by(self): qs = SouthTexasCity.objects.distance(Point(3, 3)).order_by( 'distance' diff --git a/tests/gis_tests/geo3d/tests.py b/tests/gis_tests/geo3d/tests.py index 6ef7e04f61..93c2158f6d 100644 --- a/tests/gis_tests/geo3d/tests.py +++ b/tests/gis_tests/geo3d/tests.py @@ -11,7 +11,9 @@ from django.contrib.gis.gdal import HAS_GDAL from django.contrib.gis.geos import HAS_GEOS from django.test import TestCase, ignore_warnings, skipUnlessDBFeature from django.utils._os import upath -from django.utils.deprecation import RemovedInDjango20Warning +from django.utils.deprecation import ( + RemovedInDjango20Warning, RemovedInDjango21Warning, +) if HAS_GEOS: from django.contrib.gis.db.models import Union, Extent3D @@ -172,6 +174,7 @@ class Geo3DTest(Geo3DLoadingHelper, TestCase): lm.save() self.assertEqual(3, MultiPoint3D.objects.count()) + @ignore_warnings(category=RemovedInDjango21Warning) def test_kml(self): """ Test GeoQuerySet.kml() with Z values. @@ -183,6 +186,7 @@ class Geo3DTest(Geo3DLoadingHelper, TestCase): ref_kml_regex = re.compile(r'^-95.363\d+,29.763\d+,18$') self.assertTrue(ref_kml_regex.match(h.kml)) + @ignore_warnings(category=RemovedInDjango21Warning) def test_geojson(self): """ Test GeoQuerySet.geojson() with Z values. @@ -234,6 +238,7 @@ class Geo3DTest(Geo3DLoadingHelper, TestCase): self.assertIsNone(City3D.objects.none().extent3d()) self.assertIsNone(City3D.objects.none().aggregate(Extent3D('point'))['point__extent3d']) + @ignore_warnings(category=RemovedInDjango21Warning) @skipUnlessDBFeature("supports_3d_functions") def test_perimeter(self): """ @@ -252,6 +257,7 @@ class Geo3DTest(Geo3DLoadingHelper, TestCase): Polygon3D.objects.perimeter().get(name='3D BBox').perimeter.m, tol) + @ignore_warnings(category=RemovedInDjango21Warning) @skipUnlessDBFeature("supports_3d_functions") def test_length(self): """ @@ -285,6 +291,7 @@ class Geo3DTest(Geo3DLoadingHelper, TestCase): InterstateProj3D.objects.length().get(name='I-45').length.m, tol) + @ignore_warnings(category=RemovedInDjango21Warning) @skipUnlessDBFeature("supports_3d_functions") def test_scale(self): """ @@ -297,6 +304,7 @@ class Geo3DTest(Geo3DLoadingHelper, TestCase): for city in City3D.objects.scale(1.0, 1.0, zscale): self.assertEqual(city_dict[city.name][2] * zscale, city.scale.z) + @ignore_warnings(category=RemovedInDjango21Warning) @skipUnlessDBFeature("supports_3d_functions") def test_translate(self): """ diff --git a/tests/gis_tests/geoapp/tests.py b/tests/gis_tests/geoapp/tests.py index 0acc2bd210..6ea20c0011 100644 --- a/tests/gis_tests/geoapp/tests.py +++ b/tests/gis_tests/geoapp/tests.py @@ -9,7 +9,9 @@ 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 +from django.utils.deprecation import ( + RemovedInDjango20Warning, RemovedInDjango21Warning, +) from ..utils import no_oracle, oracle, postgis, spatialite @@ -431,6 +433,7 @@ class GeoLookupTest(TestCase): @skipUnlessDBFeature("gis_enabled") +@ignore_warnings(category=RemovedInDjango21Warning) class GeoQuerySetTest(TestCase): fixtures = ['initial'] diff --git a/tests/gis_tests/geogapp/tests.py b/tests/gis_tests/geogapp/tests.py index 84b3d07931..d4f8ac3843 100644 --- a/tests/gis_tests/geogapp/tests.py +++ b/tests/gis_tests/geogapp/tests.py @@ -10,8 +10,9 @@ from django.contrib.gis.db.models.functions import Area, Distance from django.contrib.gis.gdal import HAS_GDAL from django.contrib.gis.geos import HAS_GEOS from django.contrib.gis.measure import D -from django.test import TestCase, skipUnlessDBFeature +from django.test import TestCase, ignore_warnings, skipUnlessDBFeature from django.utils._os import upath +from django.utils.deprecation import RemovedInDjango21Warning from ..utils import oracle, postgis @@ -43,6 +44,7 @@ class GeographyTest(TestCase): self.assertEqual(['Dallas', 'Houston', 'Oklahoma City'], cities) @skipUnlessDBFeature("has_distance_method", "supports_distance_geodetic") + @ignore_warnings(category=RemovedInDjango21Warning) def test03_distance_method(self): "Testing GeoQuerySet.distance() support on non-point geography fields." # `GeoQuerySet.distance` is not allowed geometry fields. @@ -95,6 +97,7 @@ class GeographyTest(TestCase): self.assertEqual(state, c.state) @skipUnlessDBFeature("has_area_method", "supports_distance_geodetic") + @ignore_warnings(category=RemovedInDjango21Warning) def test06_geography_area(self): "Testing that Area calculations work on geography columns." # SELECT ST_Area(poly) FROM geogapp_zipcode WHERE code='77002'; diff --git a/tests/runtests.py b/tests/runtests.py index db0e09c162..cc814a6d2e 100755 --- a/tests/runtests.py +++ b/tests/runtests.py @@ -158,6 +158,11 @@ def setup(verbosity, test_labels): 'django.contrib.webdesign will be removed in Django 2.0.', RemovedInDjango20Warning ) + warnings.filterwarnings( + 'ignore', + 'The GeoManager class is deprecated.', + RemovedInDjango21Warning + ) # Load all the ALWAYS_INSTALLED_APPS. django.setup()