diff --git a/django/contrib/gis/db/models/__init__.py b/django/contrib/gis/db/models/__init__.py index 5f6c2374a7..31ca0001df 100644 --- a/django/contrib/gis/db/models/__init__.py +++ b/django/contrib/gis/db/models/__init__.py @@ -1,5 +1,6 @@ from django.db.models import * # NOQA isort:skip from django.db.models import __all__ as models_all # isort:skip +import django.contrib.gis.db.models.lookups # NOQA from django.contrib.gis.db.models.aggregates import * # NOQA from django.contrib.gis.db.models.aggregates import __all__ as aggregates_all from django.contrib.gis.db.models.fields import ( diff --git a/django/contrib/gis/db/models/fields.py b/django/contrib/gis/db/models/fields.py index 9ea62400c5..77b81cddd1 100644 --- a/django/contrib/gis/db/models/fields.py +++ b/django/contrib/gis/db/models/fields.py @@ -1,9 +1,6 @@ from collections import defaultdict from django.contrib.gis import forms, gdal -from django.contrib.gis.db.models.lookups import ( - RasterBandTransform, gis_lookups, -) from django.contrib.gis.db.models.proxy import SpatialProxy from django.contrib.gis.gdal.error import GDALException from django.contrib.gis.geometry.backend import Geometry, GeometryException @@ -243,10 +240,6 @@ class BaseSpatialField(Field): return obj -for klass in gis_lookups.values(): - BaseSpatialField.register_lookup(klass) - - class GeometryField(GeoSelectFormatMixin, BaseSpatialField): """ The base Geometry field -- maps to the OpenGIS Specification Geometry type. @@ -429,6 +422,7 @@ class RasterField(BaseSpatialField): setattr(cls, self.attname, SpatialProxy(gdal.GDALRaster, self)) def get_transform(self, name): + from django.contrib.gis.db.models.lookups import RasterBandTransform try: band_index = int(name) return type( diff --git a/django/contrib/gis/db/models/lookups.py b/django/contrib/gis/db/models/lookups.py index 489eef1728..c0d1934965 100644 --- a/django/contrib/gis/db/models/lookups.py +++ b/django/contrib/gis/db/models/lookups.py @@ -1,11 +1,10 @@ import re +from django.contrib.gis.db.models.fields import BaseSpatialField from django.db.models.expressions import Col, Expression from django.db.models.lookups import Lookup, Transform from django.db.models.sql.query import Query -gis_lookups = {} - class RasterBandTransform(Transform): def as_sql(self, compiler, connection): @@ -107,6 +106,7 @@ class GISLookup(Lookup): # Geometry operators # ------------------ +@BaseSpatialField.register_lookup class OverlapsLeftLookup(GISLookup): """ The overlaps_left operator returns true if A's bounding box overlaps or is to the @@ -115,9 +115,7 @@ class OverlapsLeftLookup(GISLookup): lookup_name = 'overlaps_left' -gis_lookups['overlaps_left'] = OverlapsLeftLookup - - +@BaseSpatialField.register_lookup class OverlapsRightLookup(GISLookup): """ The 'overlaps_right' operator returns true if A's bounding box overlaps or is to the @@ -126,9 +124,7 @@ class OverlapsRightLookup(GISLookup): lookup_name = 'overlaps_right' -gis_lookups['overlaps_right'] = OverlapsRightLookup - - +@BaseSpatialField.register_lookup class OverlapsBelowLookup(GISLookup): """ The 'overlaps_below' operator returns true if A's bounding box overlaps or is below @@ -137,9 +133,7 @@ class OverlapsBelowLookup(GISLookup): lookup_name = 'overlaps_below' -gis_lookups['overlaps_below'] = OverlapsBelowLookup - - +@BaseSpatialField.register_lookup class OverlapsAboveLookup(GISLookup): """ The 'overlaps_above' operator returns true if A's bounding box overlaps or is above @@ -148,9 +142,7 @@ class OverlapsAboveLookup(GISLookup): lookup_name = 'overlaps_above' -gis_lookups['overlaps_above'] = OverlapsAboveLookup - - +@BaseSpatialField.register_lookup class LeftLookup(GISLookup): """ The 'left' operator returns true if A's bounding box is strictly to the left @@ -159,9 +151,7 @@ class LeftLookup(GISLookup): lookup_name = 'left' -gis_lookups['left'] = LeftLookup - - +@BaseSpatialField.register_lookup class RightLookup(GISLookup): """ The 'right' operator returns true if A's bounding box is strictly to the right @@ -170,9 +160,7 @@ class RightLookup(GISLookup): lookup_name = 'right' -gis_lookups['right'] = RightLookup - - +@BaseSpatialField.register_lookup class StrictlyBelowLookup(GISLookup): """ The 'strictly_below' operator returns true if A's bounding box is strictly below B's @@ -181,9 +169,7 @@ class StrictlyBelowLookup(GISLookup): lookup_name = 'strictly_below' -gis_lookups['strictly_below'] = StrictlyBelowLookup - - +@BaseSpatialField.register_lookup class StrictlyAboveLookup(GISLookup): """ The 'strictly_above' operator returns true if A's bounding box is strictly above B's @@ -192,9 +178,7 @@ class StrictlyAboveLookup(GISLookup): lookup_name = 'strictly_above' -gis_lookups['strictly_above'] = StrictlyAboveLookup - - +@BaseSpatialField.register_lookup class SameAsLookup(GISLookup): """ The "~=" operator is the "same as" operator. It tests actual geometric @@ -204,17 +188,10 @@ class SameAsLookup(GISLookup): lookup_name = 'same_as' -gis_lookups['same_as'] = SameAsLookup - - -class ExactLookup(SameAsLookup): - # Alias of same_as - lookup_name = 'exact' - - -gis_lookups['exact'] = ExactLookup +BaseSpatialField.register_lookup(SameAsLookup, 'exact') +@BaseSpatialField.register_lookup class BBContainsLookup(GISLookup): """ The 'bbcontains' operator returns true if A's bounding box completely contains @@ -223,9 +200,7 @@ class BBContainsLookup(GISLookup): lookup_name = 'bbcontains' -gis_lookups['bbcontains'] = BBContainsLookup - - +@BaseSpatialField.register_lookup class BBOverlapsLookup(GISLookup): """ The 'bboverlaps' operator returns true if A's bounding box overlaps B's bounding box. @@ -233,9 +208,7 @@ class BBOverlapsLookup(GISLookup): lookup_name = 'bboverlaps' -gis_lookups['bboverlaps'] = BBOverlapsLookup - - +@BaseSpatialField.register_lookup class ContainedLookup(GISLookup): """ The 'contained' operator returns true if A's bounding box is completely contained @@ -244,69 +217,51 @@ class ContainedLookup(GISLookup): lookup_name = 'contained' -gis_lookups['contained'] = ContainedLookup - - # ------------------ # Geometry functions # ------------------ +@BaseSpatialField.register_lookup class ContainsLookup(GISLookup): lookup_name = 'contains' -gis_lookups['contains'] = ContainsLookup - - +@BaseSpatialField.register_lookup class ContainsProperlyLookup(GISLookup): lookup_name = 'contains_properly' -gis_lookups['contains_properly'] = ContainsProperlyLookup - - +@BaseSpatialField.register_lookup class CoveredByLookup(GISLookup): lookup_name = 'coveredby' -gis_lookups['coveredby'] = CoveredByLookup - - +@BaseSpatialField.register_lookup class CoversLookup(GISLookup): lookup_name = 'covers' -gis_lookups['covers'] = CoversLookup - - +@BaseSpatialField.register_lookup class CrossesLookup(GISLookup): lookup_name = 'crosses' -gis_lookups['crosses'] = CrossesLookup - - +@BaseSpatialField.register_lookup class DisjointLookup(GISLookup): lookup_name = 'disjoint' -gis_lookups['disjoint'] = DisjointLookup - - +@BaseSpatialField.register_lookup class EqualsLookup(GISLookup): lookup_name = 'equals' -gis_lookups['equals'] = EqualsLookup - - +@BaseSpatialField.register_lookup class IntersectsLookup(GISLookup): lookup_name = 'intersects' -gis_lookups['intersects'] = IntersectsLookup - - +@BaseSpatialField.register_lookup class IsValidLookup(GISLookup): lookup_name = 'isvalid' sql_template = '%(func)s(%(lhs)s)' @@ -322,16 +277,12 @@ class IsValidLookup(GISLookup): return sql, params -gis_lookups['isvalid'] = IsValidLookup - - +@BaseSpatialField.register_lookup class OverlapsLookup(GISLookup): lookup_name = 'overlaps' -gis_lookups['overlaps'] = OverlapsLookup - - +@BaseSpatialField.register_lookup class RelateLookup(GISLookup): lookup_name = 'relate' sql_template = '%(func)s(%(lhs)s, %(rhs)s, %%s)' @@ -351,23 +302,16 @@ class RelateLookup(GISLookup): return super().get_db_prep_lookup(value, connection) -gis_lookups['relate'] = RelateLookup - - +@BaseSpatialField.register_lookup class TouchesLookup(GISLookup): lookup_name = 'touches' -gis_lookups['touches'] = TouchesLookup - - +@BaseSpatialField.register_lookup class WithinLookup(GISLookup): lookup_name = 'within' -gis_lookups['within'] = WithinLookup - - class DistanceLookupBase(GISLookup): distance = True sql_template = '%(func)s(%(lhs)s, %(rhs)s) %(op)s %(value)s' @@ -400,37 +344,27 @@ class DistanceLookupBase(GISLookup): return (rhs, params) +@BaseSpatialField.register_lookup class DWithinLookup(DistanceLookupBase): lookup_name = 'dwithin' sql_template = '%(func)s(%(lhs)s, %(rhs)s, %%s)' -gis_lookups['dwithin'] = DWithinLookup - - +@BaseSpatialField.register_lookup class DistanceGTLookup(DistanceLookupBase): lookup_name = 'distance_gt' -gis_lookups['distance_gt'] = DistanceGTLookup - - +@BaseSpatialField.register_lookup class DistanceGTELookup(DistanceLookupBase): lookup_name = 'distance_gte' -gis_lookups['distance_gte'] = DistanceGTELookup - - +@BaseSpatialField.register_lookup class DistanceLTLookup(DistanceLookupBase): lookup_name = 'distance_lt' -gis_lookups['distance_lt'] = DistanceLTLookup - - +@BaseSpatialField.register_lookup class DistanceLTELookup(DistanceLookupBase): lookup_name = 'distance_lte' - - -gis_lookups['distance_lte'] = DistanceLTELookup diff --git a/tests/gis_tests/rasterapp/test_rasterfield.py b/tests/gis_tests/rasterapp/test_rasterfield.py index e0a7ff2a5a..975b68cab0 100644 --- a/tests/gis_tests/rasterapp/test_rasterfield.py +++ b/tests/gis_tests/rasterapp/test_rasterfield.py @@ -1,9 +1,8 @@ import json +from django.contrib.gis.db.models.fields import BaseSpatialField from django.contrib.gis.db.models.functions import Distance -from django.contrib.gis.db.models.lookups import ( - DistanceLookupBase, gis_lookups, -) +from django.contrib.gis.db.models.lookups import DistanceLookupBase, GISLookup from django.contrib.gis.gdal import HAS_GDAL from django.contrib.gis.geos import GEOSGeometry from django.contrib.gis.measure import D @@ -130,7 +129,9 @@ class RasterFieldTest(TransactionTestCase): stx_pnt.transform(3086) # Loop through all the GIS lookups. - for name, lookup in gis_lookups.items(): + for name, lookup in BaseSpatialField.get_lookups().items(): + if not isinstance(lookup, GISLookup): + continue # Construct lookup filter strings. combo_keys = [ field + name for field in [