Add GeoDjango support#308
Conversation
c15694b to
3020173
Compare
ec1c627 to
3f8b0fd
Compare
edbeeaa to
de821e8
Compare
dc8bb43 to
a58fd36
Compare
c50f38c to
50550c4
Compare
e50cf56 to
7f51e94
Compare
| @@ -0,0 +1,59 @@ | |||
| # Identical to test-python-atlas.yml except that gdal-bin is also installed. | |||
There was a problem hiding this comment.
Adding both .github/workflows/test-python-geo.yml and .github/workflows/test-python-atlas-geo.yml might be redundant. (Are there any notable differences in GEO support on Atlas?) When the Atlas build ran with transactions, it was perhaps slightly more useful.
There was a problem hiding this comment.
There are no differences between Geospatial querying and indexing done on Atlas vs. what is done on local atlas.
There was a problem hiding this comment.
I meant that test-python-geo.yml uses supercharge/mongodb-github-action (is this "standalone"?) and test-python-atlas-geo.yml uses the Atlas VM.
Jibola
left a comment
There was a problem hiding this comment.
Overall, looks good! Added a few questions, and would just like a test to confirm the 5 (of 6) data types can be inserted properly (even if un-queryable at the moment)
| @@ -0,0 +1,59 @@ | |||
| # Identical to test-python-atlas.yml except that gdal-bin is also installed. | |||
There was a problem hiding this comment.
There are no differences between Geospatial querying and indexing done on Atlas vs. what is done on local atlas.
|
|
||
| - MongoDB doesn't support any spatial reference system identifiers | ||
| (:attr:`BaseSpatialField.srid <django.contrib.gis.db.models.BaseSpatialField.srid>`) | ||
| besides 4326 (WGS84) . |
There was a problem hiding this comment.
Can we add a link here?
| besides 4326 (WGS84) . | |
| besides `4326 (WGS84) <https://spatialreference.org/ref/epsg/4326/>` . |
| #. Install the necessary :doc:`Geospatial libraries | ||
| <django:ref/contrib/gis/install/geolibs>` (GEOS and GDAL). | ||
| #. Add :mod:`django.contrib.gis` to :setting:`INSTALLED_APPS` in your settings. | ||
| This is so that the ``gis`` templates can be located -- if not done, then | ||
| features such as the geographic admin or KML sitemaps will not function properly. |
There was a problem hiding this comment.
Any reservation to having it be an extension for us django-mongodb-backend[geos] or something of that nature?
There was a problem hiding this comment.
The geospatial libraries aren't Python packages.
| class GISTests(TestMixin, TransactionTestCase): | ||
| @isolate_apps("schema_") | ||
| def test_create_model(self): | ||
| """ | ||
| Spatial indexes for embedded GIS fields are created when the collections are | ||
| created. | ||
| """ | ||
| from django.contrib.gis.db.models import PointField # noqa: PLC0415 | ||
|
|
||
| class Place(EmbeddedModel): | ||
| name = models.CharField(max_length=10) | ||
| location = PointField() | ||
|
|
||
| class Meta: | ||
| app_label = "schema_" | ||
|
|
||
| class Author(models.Model): | ||
| birthplace = EmbeddedModelField(Place) | ||
|
|
||
| class Meta: |
There was a problem hiding this comment.
Can we have a test that confirms field storage and database schema? For MongoDB GeoJSON format still requires:
{ type: <GeoJSON Type>, ...} as the data format. I see in the Adapter does the work to ensure proper schema conversion, but I think it still helps to have a test.
There was a problem hiding this comment.
Like a query with pymongo.find()? Another thing to consider (if it assuages your concern) is that MongoDB raises an error (e.g. "can't extract geo keys") if a spatial index receives invalid data.
| def __init__(self, obj, geography=False): | ||
| """ | ||
| Initialize on the spatial object. | ||
| """ |
There was a problem hiding this comment.
Can you add a link to the MongoDB GeoJSON Object types in this docstring?
https://www.mongodb.com/docs/manual/reference/geojson/
7 out of 7 work (PointField, LineStringField, PolygonField, MultiPointField, MultiLineStringField, MultiPolygonField, GeometryCollectionField). It's tested in Django's test suite. Surprisingly, I had to add some tests for a few of the fields to our Django fork: Add tests for MultiPointField, MultiLineStringField, and GeometryCollectionFieldcommit 9dc4d37d7e8bb514922bd51c1a0940f5537e2c35
Author: Tim Graham <timograham@gmail.com>
Date: Fri Jul 25 15:41:50 2025 -0400
Add tests for MultiPointField, MultiLineStringField, and GeometryCollectionField
These should be contributed upstream to Django.
diff --git a/tests/gis_tests/geoapp/models.py b/tests/gis_tests/geoapp/models.py
index 2c13c827c6..c7acb653d3 100644
--- a/tests/gis_tests/geoapp/models.py
+++ b/tests/gis_tests/geoapp/models.py
@@ -102,3 +102,15 @@ class ManyPointModel(NamedModel):
point1 = models.PointField()
point2 = models.PointField()
point3 = models.PointField(srid=3857)
+
+
+class Points(models.Model):
+ geom = models.MultiPointField()
+
+
+class Lines(models.Model):
+ geom = models.MultiLineStringField()
+
+
+class GeometryCollections(models.Model):
+ geom = models.GeometryCollectionField()
diff --git a/tests/gis_tests/geoapp/tests.py b/tests/gis_tests/geoapp/tests.py
index f9abae129f..945d80a563 100644
--- a/tests/gis_tests/geoapp/tests.py
+++ b/tests/gis_tests/geoapp/tests.py
@@ -26,10 +26,13 @@ from .models import (
City,
Country,
Feature,
+ GeometryCollections,
+ Lines,
MinusOneSRID,
MultiFields,
NonConcreteModel,
PennsylvaniaCity,
+ Points,
State,
ThreeDimensionalFeature,
Track,
@@ -269,6 +272,47 @@ class GeoModelTest(TestCase):
self.assertEqual(feature.geom.srid, g.srid)
+# TODO: contribute these tests added to the MongoDB fork upstream to Django.
+class SaveLoadTests(TestCase):
+ def test_multi_line_string_field(self):
+ geom = MultiLineString(
+ LineString((0, 0), (1, 1), (5, 5)),
+ LineString((0, 0), (0, 5), (5, 5), (5, 0), (0, 0)),
+ )
+ obj = Lines.objects.create(geom=geom)
+ obj.refresh_from_db()
+ self.assertEqual(obj.geom.tuple, geom.tuple)
+
+ def test_multi_line_string_with_linear_ring(self):
+ # LinearRings are transformed to LineString
+ geom = MultiLineString(
+ LineString((0, 0), (1, 1), (5, 5)),
+ LinearRing((0, 0), (0, 5), (5, 5), (5, 0), (0, 0)),
+ )
+ obj = Lines.objects.create(geom=geom)
+ obj.refresh_from_db()
+ self.assertEqual(obj.geom.tuple, geom.tuple)
+ self.assertEqual(obj.geom[0].tuple, geom[0].tuple)
+ self.assertEqual(obj.geom[1].__class__.__name__, "LineString")
+ self.assertEqual(obj.geom[1].tuple, geom[1].tuple)
+
+ def test_multi_point_field(self):
+ geom = MultiPoint(Point(1, 1), Point(0, 0))
+ obj = Points.objects.create(geom=geom)
+ obj.refresh_from_db()
+ self.assertEqual(obj.geom, geom)
+
+ def test_geometry_collection_field(self):
+ geom = GeometryCollection(
+ Point(2, 2),
+ LineString((0, 0), (2, 2)),
+ Polygon(LinearRing((0, 0), (0, 5), (5, 5), (5, 0), (0, 0))),
+ )
+ obj = GeometryCollections.objects.create(geom=geom)
+ obj.refresh_from_db()
+ self.assertEqual(obj.geom, geom)
+
+
class GeoLookupTest(TestCase):
fixtures = ["initial"] |
|
|
||
| The :ref:`geospatial aggregation stage <geospatial-operators>` only supports | ||
| one operator: :doc:`$geoNear <manual:reference/operator/aggregation/geoNear>`. | ||
| You can use it :meth:`.raw_aggregate` queries. |
No description provided.