1
0
mirror of https://github.com/django/django.git synced 2025-09-24 23:49:12 +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 committed by Jacob Walls
parent 6fe96639ba
commit e20e189045
7 changed files with 36 additions and 7 deletions

View File

@ -73,6 +73,7 @@ class SpatiaLiteOperations(BaseSpatialOperations, DatabaseOperations):
"ForcePolygonCW": "ST_ForceLHR", "ForcePolygonCW": "ST_ForceLHR",
"FromWKB": "ST_GeomFromWKB", "FromWKB": "ST_GeomFromWKB",
"FromWKT": "ST_GeomFromText", "FromWKT": "ST_GeomFromText",
"IsEmpty": "ST_IsEmpty",
"Length": "ST_Length", "Length": "ST_Length",
"LineLocatePoint": "ST_Line_Locate_Point", "LineLocatePoint": "ST_Line_Locate_Point",
"NumPoints": "ST_NPoints", "NumPoints": "ST_NPoints",
@ -84,7 +85,7 @@ class SpatiaLiteOperations(BaseSpatialOperations, DatabaseOperations):
@cached_property @cached_property
def unsupported_functions(self): def unsupported_functions(self):
unsupported = {"GeometryDistance", "IsEmpty", "MemSize", "Rotate"} unsupported = {"GeometryDistance", "MemSize", "Rotate"}
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

@ -451,6 +451,10 @@ class IsEmpty(GeoFuncMixin, Transform):
lookup_name = "isempty" lookup_name = "isempty"
output_field = BooleanField() output_field = BooleanField()
def as_sqlite(self, compiler, connection, **extra_context):
sql, params = super().as_sql(compiler, connection, **extra_context)
return "NULLIF(%s, -1)" % sql, params
@BaseSpatialField.register_lookup @BaseSpatialField.register_lookup
class IsValid(OracleToleranceMixin, GeoFuncMixin, Transform): class IsValid(OracleToleranceMixin, GeoFuncMixin, Transform):

View File

@ -363,7 +363,7 @@ Lookup Type PostGIS Oracle MariaDB MySQL [#]_
:lookup:`exact <same_as>` X X X X X B :lookup:`exact <same_as>` X X X X X B
:lookup:`geom_type` X X (≥ 23c) X X X :lookup:`geom_type` X X (≥ 23c) X X X
: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 (≥ 12.0.1) X X :lookup:`isvalid` X X X (≥ 12.0.1) 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
@ -414,7 +414,7 @@ Function PostGIS Oracle MariaDB MySQL
:class:`GeometryDistance` X :class:`GeometryDistance` X
:class:`GeometryType` X X (≥ 23c) X X X :class:`GeometryType` X X (≥ 23c) X X X
: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 (≥ 12.0.1) X X :class:`IsValid` X X X (≥ 12.0.1) 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

@ -621,11 +621,16 @@ Miscellaneous
.. 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:: 6.1
SpatiaLite support was added.
``IsValid`` ``IsValid``
----------- -----------

View File

@ -361,7 +361,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.
@ -369,6 +370,10 @@ Example::
Zipcode.objects.filter(poly__isempty=True) Zipcode.objects.filter(poly__isempty=True)
.. versionchanged:: 6.1
SpatiaLite support was added.
.. fieldlookup:: isvalid .. fieldlookup:: isvalid
``isvalid`` ``isvalid``

View File

@ -53,7 +53,9 @@ Minor features
:mod:`django.contrib.gis` :mod:`django.contrib.gis`
~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~
* ... * The :lookup:`isempty` lookup and
:class:`IsEmpty() <django.contrib.gis.db.models.functions.IsEmpty>`
database function are now supported on SpatiaLite.
:mod:`django.contrib.messages` :mod:`django.contrib.messages`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -431,7 +431,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_geometry_empty(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(
@ -442,6 +442,18 @@ 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_geometry_null(self):
nowhere = State.objects.create(name="Nowhere", poly=None)
qs = State.objects.annotate(isempty=functions.IsEmpty("poly"))
self.assertSequenceEqual(qs.filter(isempty=None), [nowhere])
self.assertSequenceEqual(
qs.filter(isempty=False).order_by("name").values_list("name", flat=True),
["Colorado", "Kansas"],
)
self.assertSequenceEqual(qs.filter(isempty=True), [])
self.assertSequenceEqual(State.objects.filter(poly__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))")