diff --git a/django/contrib/gis/db/backends/mysql/operations.py b/django/contrib/gis/db/backends/mysql/operations.py index e808609802..44717f5e30 100644 --- a/django/contrib/gis/db/backends/mysql/operations.py +++ b/django/contrib/gis/db/backends/mysql/operations.py @@ -29,7 +29,7 @@ class MySQLOperations(BaseSpatialOperations, DatabaseOperations): @cached_property def gis_operators(self): - return { + operators = { 'bbcontains': SpatialOperator(func='MBRContains'), # For consistency w/PostGIS API 'bboverlaps': SpatialOperator(func='MBROverlaps'), # ... 'contained': SpatialOperator(func='MBRWithin'), # ... @@ -44,6 +44,9 @@ class MySQLOperations(BaseSpatialOperations, DatabaseOperations): 'touches': SpatialOperator(func='ST_Touches'), 'within': SpatialOperator(func='ST_Within'), } + if self.connection.mysql_is_mariadb: + operators['relate'] = SpatialOperator(func='ST_Relate') + return operators disallowed_aggregates = ( aggregates.Collect, aggregates.Extent, aggregates.Extent3D, diff --git a/docs/ref/contrib/gis/db-api.txt b/docs/ref/contrib/gis/db-api.txt index ad896f3e5d..e7bd672914 100644 --- a/docs/ref/contrib/gis/db-api.txt +++ b/docs/ref/contrib/gis/db-api.txt @@ -332,7 +332,7 @@ Lookup Type PostGIS Oracle MariaDB MySQL [#]_ Sp :lookup:`intersects` X X X X X B :lookup:`isvalid` X X X (≥ 5.7.5) X (LWGEOM) :lookup:`overlaps` X X X X X B -:lookup:`relate` X X X C +:lookup:`relate` X X X X C :lookup:`same_as` X X X X X B :lookup:`touches` X X X X X B :lookup:`within` X X X X X B diff --git a/docs/ref/contrib/gis/geoquerysets.txt b/docs/ref/contrib/gis/geoquerysets.txt index 6f191c0af4..7b5cae6e30 100644 --- a/docs/ref/contrib/gis/geoquerysets.txt +++ b/docs/ref/contrib/gis/geoquerysets.txt @@ -426,14 +426,15 @@ SpatiaLite ``Overlaps(poly, geom)`` ---------- *Availability*: `PostGIS `__, -Oracle, SpatiaLite, PGRaster (Conversion) +MariaDB, Oracle, SpatiaLite, PGRaster (Conversion) Tests if the geometry field is spatially related to the lookup geometry by the values given in the given pattern. This lookup requires a tuple parameter, ``(geom, pattern)``; the form of ``pattern`` will depend on the spatial backend: -PostGIS & SpatiaLite -~~~~~~~~~~~~~~~~~~~~ +MariaDB, PostGIS, and SpatiaLite +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + On these spatial backends the intersection pattern is a string comprising nine characters, which define intersections between the interior, boundary, and exterior of the geometry field and the lookup geometry. @@ -447,7 +448,7 @@ Geometry example:: # the intersection pattern (the pattern here is for 'contains'). Zipcode.objects.filter(poly__relate=(geom, 'T*T***FF*')) -PostGIS SQL equivalent: +PostGIS and MariaDB SQL equivalent: .. code-block:: sql @@ -471,6 +472,10 @@ PostGIS SQL equivalent: SELECT ... WHERE ST_Relate(poly, ST_Polygon(rast, 1), 'T*T***FF*') SELECT ... WHERE ST_Relate(ST_Polygon(rast, 2), ST_Polygon(rast, 1), 'T*T***FF*') +.. versionchanged:: 3.1 + + MariaDB support was added. + Oracle ~~~~~~ diff --git a/docs/releases/3.1.txt b/docs/releases/3.1.txt index 1077fcb921..a42d71c855 100644 --- a/docs/releases/3.1.txt +++ b/docs/releases/3.1.txt @@ -59,7 +59,7 @@ Minor features :mod:`django.contrib.gis` ~~~~~~~~~~~~~~~~~~~~~~~~~ -* ... +* :lookup:`relate` lookup is now supported on MariaDB. :mod:`django.contrib.messages` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/tests/gis_tests/geoapp/tests.py b/tests/gis_tests/geoapp/tests.py index c97bce5f32..771bfb0903 100644 --- a/tests/gis_tests/geoapp/tests.py +++ b/tests/gis_tests/geoapp/tests.py @@ -450,7 +450,7 @@ class GeoLookupTest(TestCase): qs.count() # Relate works differently for the different backends. - if postgis or spatialite: + if postgis or spatialite or mariadb: contains_mask = 'T*T***FF*' within_mask = 'T*F**F***' intersects_mask = 'T********' @@ -461,7 +461,11 @@ class GeoLookupTest(TestCase): intersects_mask = 'overlapbdyintersect' # Testing contains relation mask. - self.assertEqual('Texas', Country.objects.get(mpoly__relate=(pnt1, contains_mask)).name) + if connection.features.supports_transform: + self.assertEqual( + Country.objects.get(mpoly__relate=(pnt1, contains_mask)).name, + 'Texas', + ) self.assertEqual('Texas', Country.objects.get(mpoly__relate=(pnt2, contains_mask)).name) # Testing within relation mask. @@ -470,7 +474,11 @@ class GeoLookupTest(TestCase): # Testing intersection relation mask. if not oracle: - self.assertEqual('Texas', Country.objects.get(mpoly__relate=(pnt1, intersects_mask)).name) + if connection.features.supports_transform: + self.assertEqual( + Country.objects.get(mpoly__relate=(pnt1, intersects_mask)).name, + 'Texas', + ) self.assertEqual('Texas', Country.objects.get(mpoly__relate=(pnt2, intersects_mask)).name) self.assertEqual('Lawrence', City.objects.get(point__relate=(ks.poly, intersects_mask)).name)