1
0
mirror of https://github.com/django/django.git synced 2024-12-23 01:25:58 +00:00

Refs #33783 -- Added IsEmpty GIS database function and __isempty lookup on SpatiaLite.

This commit is contained in:
David Smith 2024-09-29 16:53:46 +01:00
parent 43287cbb87
commit 1e67d1a061
7 changed files with 59 additions and 7 deletions

View File

@ -82,7 +82,7 @@ class SpatiaLiteOperations(BaseSpatialOperations, DatabaseOperations):
@cached_property @cached_property
def unsupported_functions(self): def unsupported_functions(self):
unsupported = {"GeometryDistance", "IsEmpty", "MemSize"} unsupported = {"GeometryDistance", "MemSize"}
if not self.geom_lib_version(): if not self.geom_lib_version():
unsupported |= {"Azimuth", "GeoHash", "MakeValid"} unsupported |= {"Azimuth", "GeoHash", "MakeValid"}
if self.spatial_version < (5, 1): if self.spatial_version < (5, 1):

View File

@ -359,7 +359,7 @@ Lookup Type PostGIS Oracle MariaDB MySQL [#]_ S
:lookup:`equals` X X X X X C :lookup:`equals` X X X X X C
:lookup:`exact <same_as>` X X X X X B :lookup:`exact <same_as>` X X X X X B
:lookup:`intersects` X X X X X B :lookup:`intersects` X X X X X B
:lookup:`isempty` X :lookup:`isempty` X X
:lookup:`isvalid` X X X (≥ 11.7) X X :lookup:`isvalid` X X X (≥ 11.7) X X
:lookup:`overlaps` X X X X X B :lookup:`overlaps` X X X X X B
:lookup:`relate` X X X X C :lookup:`relate` X X X X C
@ -408,7 +408,7 @@ Function PostGIS Oracle MariaDB MySQL
:class:`FromWKT` X X X X X :class:`FromWKT` X X X X X
:class:`GeoHash` X X (≥ 11.7) X X (LWGEOM/RTTOPO) :class:`GeoHash` X X (≥ 11.7) X X (LWGEOM/RTTOPO)
:class:`Intersection` X X X X X :class:`Intersection` X X X X X
:class:`IsEmpty` X :class:`IsEmpty` X X
:class:`IsValid` X X X (≥ 11.7) X X :class:`IsValid` X X X (≥ 11.7) X X
:class:`Length` X X X X X :class:`Length` X X X X X
:class:`LineLocatePoint` X X :class:`LineLocatePoint` X X

View File

@ -438,11 +438,16 @@ intersection between them.
.. class:: IsEmpty(expr) .. class:: IsEmpty(expr)
*Availability*: `PostGIS <https://postgis.net/docs/ST_IsEmpty.html>`__ *Availability*: `PostGIS <https://postgis.net/docs/ST_IsEmpty.html>`__,
SpatiaLite
Accepts a geographic field or expression and tests if the value is an empty Accepts a geographic field or expression and tests if the value is an empty
geometry. Returns ``True`` if its value is empty and ``False`` otherwise. geometry. Returns ``True`` if its value is empty and ``False`` otherwise.
.. versionchanged:: 5.2
Support for SpatiaLite was added.
``IsValid`` ``IsValid``
=========== ===========

View File

@ -362,7 +362,8 @@ SpatiaLite ``Intersects(poly, geom)``
``isempty`` ``isempty``
----------- -----------
*Availability*: `PostGIS <https://postgis.net/docs/ST_IsEmpty.html>`__ *Availability*: `PostGIS <https://postgis.net/docs/ST_IsEmpty.html>`__,
SpatiaLite
Tests if the geometry is empty. Tests if the geometry is empty.
@ -372,6 +373,10 @@ Example::
.. fieldlookup:: isvalid .. fieldlookup:: isvalid
.. versionchanged:: 5.2
Support for SpatiaLite was added.
``isvalid`` ``isvalid``
----------- -----------

View File

@ -108,6 +108,10 @@ Minor features
:class:`~django.contrib.gis.db.models.functions.IsValid` database functions :class:`~django.contrib.gis.db.models.functions.IsValid` database functions
are now supported on MariaDB 11.7+. are now supported on MariaDB 11.7+.
* The :lookup:`isempty` lookup and
:class:`~django.contrib.gis.db.models.functions.IsEmpty` expression are now
supported on SpatiaLite.
:mod:`django.contrib.messages` :mod:`django.contrib.messages`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -28,6 +28,13 @@ class City(NamedModel):
app_label = "geoapp" app_label = "geoapp"
class Town(NamedModel):
point = models.PointField(null=True)
class Meta:
app_label = "geoapp"
# This is an inherited model from City # This is an inherited model from City
class PennsylvaniaCity(City): class PennsylvaniaCity(City):
county = models.CharField(max_length=30) county = models.CharField(max_length=30)

View File

@ -11,7 +11,15 @@ from django.db.models import IntegerField, Sum, Value
from django.test import TestCase, skipUnlessDBFeature from django.test import TestCase, skipUnlessDBFeature
from ..utils import FuncTestMixin from ..utils import FuncTestMixin
from .models import City, Country, CountryWebMercator, ManyPointModel, State, Track from .models import (
City,
Country,
CountryWebMercator,
ManyPointModel,
State,
Town,
Track,
)
class GISFunctionsTests(FuncTestMixin, TestCase): class GISFunctionsTests(FuncTestMixin, TestCase):
@ -413,7 +421,7 @@ class GISFunctionsTests(FuncTestMixin, TestCase):
self.assertIs(c.inter.empty, True) self.assertIs(c.inter.empty, True)
@skipUnlessDBFeature("supports_empty_geometries", "has_IsEmpty_function") @skipUnlessDBFeature("supports_empty_geometries", "has_IsEmpty_function")
def test_isempty(self): def test_isempty_empty_geometry(self):
empty = City.objects.create(name="Nowhere", point=Point(srid=4326)) empty = City.objects.create(name="Nowhere", point=Point(srid=4326))
City.objects.create(name="Somewhere", point=Point(6.825, 47.1, srid=4326)) City.objects.create(name="Somewhere", point=Point(6.825, 47.1, srid=4326))
self.assertSequenceEqual( self.assertSequenceEqual(
@ -424,6 +432,29 @@ class GISFunctionsTests(FuncTestMixin, TestCase):
) )
self.assertSequenceEqual(City.objects.filter(point__isempty=True), [empty]) self.assertSequenceEqual(City.objects.filter(point__isempty=True), [empty])
@skipUnlessDBFeature("has_IsEmpty_function")
def test_isempty_null_geometry(self):
null_geometry = Town.objects.create(name="Nowhere", point=None)
Town.objects.create(name="Somewhere", point=Point(6.825, 47.1, srid=4326))
if connection.ops.spatialite:
self.assertSequenceEqual(
Town.objects.annotate(isempty=functions.IsEmpty("point")).filter(
isempty=True
),
[null_geometry],
)
self.assertSequenceEqual(
Town.objects.filter(point__isempty=True), [null_geometry]
)
else:
self.assertSequenceEqual(
Town.objects.annotate(isempty=functions.IsEmpty("point")).filter(
isempty=True
),
[],
)
self.assertSequenceEqual(Town.objects.filter(point__isempty=True), [])
@skipUnlessDBFeature("has_IsValid_function") @skipUnlessDBFeature("has_IsValid_function")
def test_isvalid(self): def test_isvalid(self):
valid_geom = fromstr("POLYGON((0 0, 0 1, 1 1, 1 0, 0 0))") valid_geom = fromstr("POLYGON((0 0, 0 1, 1 1, 1 0, 0 0))")