Deprecated legacy GeoManager/GeoQuerySet methods

This commit is contained in:
Claude Paroz 2015-01-29 20:12:08 +01:00
parent 71e20814fc
commit ea27e26b09
10 changed files with 70 additions and 9 deletions

View File

@ -1,5 +1,8 @@
import warnings
from django.contrib.gis.db.models.query import GeoQuerySet from django.contrib.gis.db.models.query import GeoQuerySet
from django.db.models.manager import Manager from django.db.models.manager import Manager
from django.utils.deprecation import RemovedInDjango21Warning
class GeoManager(Manager.from_queryset(GeoQuerySet)): 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 # so that geometry columns on Oracle and MySQL are selected
# properly. # properly.
use_for_related_fields = True 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)

View File

@ -15,7 +15,9 @@ from django.db.models.expressions import RawSQL
from django.db.models.fields import Field from django.db.models.fields import Field
from django.db.models.query import QuerySet from django.db.models.query import QuerySet
from django.utils import six from django.utils import six
from django.utils.deprecation import RemovedInDjango20Warning from django.utils.deprecation import (
RemovedInDjango20Warning, RemovedInDjango21Warning,
)
class GeoQuerySet(QuerySet): class GeoQuerySet(QuerySet):
@ -513,6 +515,11 @@ class GeoQuerySet(QuerySet):
The name of the model attribute to attach the output of The name of the model attribute to attach the output of
the spatial function to. 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. # Default settings.
settings.setdefault('desc', None) settings.setdefault('desc', None)
settings.setdefault('geom_args', ()) settings.setdefault('geom_args', ())

View File

@ -2,6 +2,7 @@ from __future__ import unicode_literals
from django.apps import apps from django.apps import apps
from django.contrib.gis.db.models.fields import GeometryField 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.contrib.gis.shortcuts import render_to_kml, render_to_kmz
from django.core.exceptions import FieldDoesNotExist from django.core.exceptions import FieldDoesNotExist
from django.db import DEFAULT_DB_ALIAS, connections 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] connection = connections[using]
if connection.features.has_kml_method: if connection.features.has_AsKML_function:
# Database will take care of transformation. # 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: else:
# If the database offers no KML method, we use the `kml` # If the database offers no KML method, we use the `kml`
# attribute of the lazy geometry instead. # attribute of the lazy geometry instead.
placemarks = [] placemarks = []
if connection.features.has_transform_method: if connection.features.has_Transform_function:
qs = klass._default_manager.using(using).transform(4326, field_name=field_name) qs = klass._default_manager.using(using).annotate(
**{'%s_4326' % field_name: Transform(field_name, 4326)})
field_name += '_4326'
else: else:
qs = klass._default_manager.using(using).all() qs = klass._default_manager.using(using).all()
for mod in qs: for mod in qs:

View File

@ -35,6 +35,8 @@ details on these changes.
* The ``django.contrib.auth.tests.utils.skipIfCustomUser()`` decorator will be * The ``django.contrib.auth.tests.utils.skipIfCustomUser()`` decorator will be
removed. removed.
* The ``GeoManager`` and ``GeoQuerySet`` classes will be removed.
.. _deprecation-removed-in-2.0: .. _deprecation-removed-in-2.0:
2.0 2.0

View File

@ -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. 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 Miscellaneous
~~~~~~~~~~~~~ ~~~~~~~~~~~~~

View File

@ -7,7 +7,8 @@ from django.contrib.gis.geos import HAS_GEOS
from django.contrib.gis.measure import D # alias for Distance from django.contrib.gis.measure import D # alias for Distance
from django.db import connection from django.db import connection
from django.db.models import Q 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 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)))) self.assertListEqual(au_cities, self.get_names(qs.filter(point__dwithin=(self.au_pnt, dist))))
@skipUnlessDBFeature("has_distance_method") @skipUnlessDBFeature("has_distance_method")
@ignore_warnings(category=RemovedInDjango21Warning)
def test_distance_projected(self): def test_distance_projected(self):
""" """
Test the `distance` GeoQuerySet method on projected coordinate systems. 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) self.assertAlmostEqual(ft_distances[i], c.distance.survey_ft, tol)
@skipUnlessDBFeature("has_distance_method", "supports_distance_geodetic") @skipUnlessDBFeature("has_distance_method", "supports_distance_geodetic")
@ignore_warnings(category=RemovedInDjango21Warning)
def test_distance_geodetic(self): def test_distance_geodetic(self):
""" """
Test the `distance` GeoQuerySet method on geodetic coordinate systems. Test the `distance` GeoQuerySet method on geodetic coordinate systems.
@ -202,6 +205,7 @@ class DistanceTest(TestCase):
@no_oracle # Oracle already handles geographic distance calculation. @no_oracle # Oracle already handles geographic distance calculation.
@skipUnlessDBFeature("has_distance_method") @skipUnlessDBFeature("has_distance_method")
@ignore_warnings(category=RemovedInDjango21Warning)
def test_distance_transform(self): def test_distance_transform(self):
""" """
Test the `distance` GeoQuerySet method used with `transform` on a geographic field. 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']) self.assertEqual(cities, ['Adelaide', 'Hobart', 'Shellharbour', 'Thirroul'])
@skipUnlessDBFeature("has_area_method") @skipUnlessDBFeature("has_area_method")
@ignore_warnings(category=RemovedInDjango21Warning)
def test_area(self): def test_area(self):
""" """
Test the `area` GeoQuerySet method. Test the `area` GeoQuerySet method.
@ -335,6 +340,7 @@ class DistanceTest(TestCase):
self.assertAlmostEqual(area_sq_m[i], z.area.sq_m, tol) self.assertAlmostEqual(area_sq_m[i], z.area.sq_m, tol)
@skipUnlessDBFeature("has_length_method") @skipUnlessDBFeature("has_length_method")
@ignore_warnings(category=RemovedInDjango21Warning)
def test_length(self): def test_length(self):
""" """
Test the `length` GeoQuerySet method. Test the `length` GeoQuerySet method.
@ -358,6 +364,7 @@ class DistanceTest(TestCase):
self.assertAlmostEqual(len_m2, i10.length.m, 2) self.assertAlmostEqual(len_m2, i10.length.m, 2)
@skipUnlessDBFeature("has_perimeter_method") @skipUnlessDBFeature("has_perimeter_method")
@ignore_warnings(category=RemovedInDjango21Warning)
def test_perimeter(self): def test_perimeter(self):
""" """
Test the `perimeter` GeoQuerySet method. Test the `perimeter` GeoQuerySet method.
@ -374,6 +381,7 @@ class DistanceTest(TestCase):
self.assertEqual(0, c.perim.m) self.assertEqual(0, c.perim.m)
@skipUnlessDBFeature("has_area_method", "has_distance_method") @skipUnlessDBFeature("has_area_method", "has_distance_method")
@ignore_warnings(category=RemovedInDjango21Warning)
def test_measurement_null_fields(self): def test_measurement_null_fields(self):
""" """
Test the measurement GeoQuerySet methods on fields with NULL values. Test the measurement GeoQuerySet methods on fields with NULL values.
@ -388,6 +396,7 @@ class DistanceTest(TestCase):
self.assertIsNone(z.area) self.assertIsNone(z.area)
@skipUnlessDBFeature("has_distance_method") @skipUnlessDBFeature("has_distance_method")
@ignore_warnings(category=RemovedInDjango21Warning)
def test_distance_order_by(self): def test_distance_order_by(self):
qs = SouthTexasCity.objects.distance(Point(3, 3)).order_by( qs = SouthTexasCity.objects.distance(Point(3, 3)).order_by(
'distance' 'distance'

View File

@ -11,7 +11,9 @@ from django.contrib.gis.gdal import HAS_GDAL
from django.contrib.gis.geos import HAS_GEOS from django.contrib.gis.geos import HAS_GEOS
from django.test import TestCase, ignore_warnings, skipUnlessDBFeature from django.test import TestCase, ignore_warnings, skipUnlessDBFeature
from django.utils._os import upath from django.utils._os import upath
from django.utils.deprecation import RemovedInDjango20Warning from django.utils.deprecation import (
RemovedInDjango20Warning, RemovedInDjango21Warning,
)
if HAS_GEOS: if HAS_GEOS:
from django.contrib.gis.db.models import Union, Extent3D from django.contrib.gis.db.models import Union, Extent3D
@ -172,6 +174,7 @@ class Geo3DTest(Geo3DLoadingHelper, TestCase):
lm.save() lm.save()
self.assertEqual(3, MultiPoint3D.objects.count()) self.assertEqual(3, MultiPoint3D.objects.count())
@ignore_warnings(category=RemovedInDjango21Warning)
def test_kml(self): def test_kml(self):
""" """
Test GeoQuerySet.kml() with Z values. Test GeoQuerySet.kml() with Z values.
@ -183,6 +186,7 @@ class Geo3DTest(Geo3DLoadingHelper, TestCase):
ref_kml_regex = re.compile(r'^<Point><coordinates>-95.363\d+,29.763\d+,18</coordinates></Point>$') ref_kml_regex = re.compile(r'^<Point><coordinates>-95.363\d+,29.763\d+,18</coordinates></Point>$')
self.assertTrue(ref_kml_regex.match(h.kml)) self.assertTrue(ref_kml_regex.match(h.kml))
@ignore_warnings(category=RemovedInDjango21Warning)
def test_geojson(self): def test_geojson(self):
""" """
Test GeoQuerySet.geojson() with Z values. 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().extent3d())
self.assertIsNone(City3D.objects.none().aggregate(Extent3D('point'))['point__extent3d']) self.assertIsNone(City3D.objects.none().aggregate(Extent3D('point'))['point__extent3d'])
@ignore_warnings(category=RemovedInDjango21Warning)
@skipUnlessDBFeature("supports_3d_functions") @skipUnlessDBFeature("supports_3d_functions")
def test_perimeter(self): def test_perimeter(self):
""" """
@ -252,6 +257,7 @@ class Geo3DTest(Geo3DLoadingHelper, TestCase):
Polygon3D.objects.perimeter().get(name='3D BBox').perimeter.m, Polygon3D.objects.perimeter().get(name='3D BBox').perimeter.m,
tol) tol)
@ignore_warnings(category=RemovedInDjango21Warning)
@skipUnlessDBFeature("supports_3d_functions") @skipUnlessDBFeature("supports_3d_functions")
def test_length(self): def test_length(self):
""" """
@ -285,6 +291,7 @@ class Geo3DTest(Geo3DLoadingHelper, TestCase):
InterstateProj3D.objects.length().get(name='I-45').length.m, InterstateProj3D.objects.length().get(name='I-45').length.m,
tol) tol)
@ignore_warnings(category=RemovedInDjango21Warning)
@skipUnlessDBFeature("supports_3d_functions") @skipUnlessDBFeature("supports_3d_functions")
def test_scale(self): def test_scale(self):
""" """
@ -297,6 +304,7 @@ class Geo3DTest(Geo3DLoadingHelper, TestCase):
for city in City3D.objects.scale(1.0, 1.0, zscale): for city in City3D.objects.scale(1.0, 1.0, zscale):
self.assertEqual(city_dict[city.name][2] * zscale, city.scale.z) self.assertEqual(city_dict[city.name][2] * zscale, city.scale.z)
@ignore_warnings(category=RemovedInDjango21Warning)
@skipUnlessDBFeature("supports_3d_functions") @skipUnlessDBFeature("supports_3d_functions")
def test_translate(self): def test_translate(self):
""" """

View File

@ -9,7 +9,9 @@ from django.core.management import call_command
from django.db import connection from django.db import connection
from django.test import TestCase, ignore_warnings, skipUnlessDBFeature from django.test import TestCase, ignore_warnings, skipUnlessDBFeature
from django.utils import six 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 from ..utils import no_oracle, oracle, postgis, spatialite
@ -431,6 +433,7 @@ class GeoLookupTest(TestCase):
@skipUnlessDBFeature("gis_enabled") @skipUnlessDBFeature("gis_enabled")
@ignore_warnings(category=RemovedInDjango21Warning)
class GeoQuerySetTest(TestCase): class GeoQuerySetTest(TestCase):
fixtures = ['initial'] fixtures = ['initial']

View File

@ -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.gdal import HAS_GDAL
from django.contrib.gis.geos import HAS_GEOS from django.contrib.gis.geos import HAS_GEOS
from django.contrib.gis.measure import D 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._os import upath
from django.utils.deprecation import RemovedInDjango21Warning
from ..utils import oracle, postgis from ..utils import oracle, postgis
@ -43,6 +44,7 @@ class GeographyTest(TestCase):
self.assertEqual(['Dallas', 'Houston', 'Oklahoma City'], cities) self.assertEqual(['Dallas', 'Houston', 'Oklahoma City'], cities)
@skipUnlessDBFeature("has_distance_method", "supports_distance_geodetic") @skipUnlessDBFeature("has_distance_method", "supports_distance_geodetic")
@ignore_warnings(category=RemovedInDjango21Warning)
def test03_distance_method(self): def test03_distance_method(self):
"Testing GeoQuerySet.distance() support on non-point geography fields." "Testing GeoQuerySet.distance() support on non-point geography fields."
# `GeoQuerySet.distance` is not allowed geometry fields. # `GeoQuerySet.distance` is not allowed geometry fields.
@ -95,6 +97,7 @@ class GeographyTest(TestCase):
self.assertEqual(state, c.state) self.assertEqual(state, c.state)
@skipUnlessDBFeature("has_area_method", "supports_distance_geodetic") @skipUnlessDBFeature("has_area_method", "supports_distance_geodetic")
@ignore_warnings(category=RemovedInDjango21Warning)
def test06_geography_area(self): def test06_geography_area(self):
"Testing that Area calculations work on geography columns." "Testing that Area calculations work on geography columns."
# SELECT ST_Area(poly) FROM geogapp_zipcode WHERE code='77002'; # SELECT ST_Area(poly) FROM geogapp_zipcode WHERE code='77002';

View File

@ -158,6 +158,11 @@ def setup(verbosity, test_labels):
'django.contrib.webdesign will be removed in Django 2.0.', 'django.contrib.webdesign will be removed in Django 2.0.',
RemovedInDjango20Warning RemovedInDjango20Warning
) )
warnings.filterwarnings(
'ignore',
'The GeoManager class is deprecated.',
RemovedInDjango21Warning
)
# Load all the ALWAYS_INSTALLED_APPS. # Load all the ALWAYS_INSTALLED_APPS.
django.setup() django.setup()