2

Take this model:

from django.contrib.gis.db import models


class Location(models.Model):
    point = models.PointField(null=True, blank=True)

Then try to execute this, deliberately giving it a wrong SRID:

from django.contrib.gis.geos import Point
from testpoint.models import Location

some_location = Location()
some_location.point = Point(x=15, y=16, srid=210)
some_location.save()

Upon executing the last statement, some_location.save(), the message "unknown SRID: 210" is shown on the console. So far so good. The problem is that the .save() returns successfully, and null is stored in point; but what I want is for nothing to be saved and for an exception to be raised.

Django appears to send this SQL to spatialite:

INSERT INTO "testpoint_location" ("point")
VALUES (Transform(GeomFromText('POINT(15.0 16.0)', 210), 4326))

and spatialite appears to execute it (with the warning printed on the console) without ever telling Django that something went wrong.

How can I tell spatialite to fail and return an error when the SRID is wrong?

John Moutafis
  • 22,254
  • 11
  • 68
  • 112
Antonis Christofides
  • 6,990
  • 2
  • 39
  • 57

1 Answers1

0

On GeoDjango Database API documentation states that:

Moreover, if the GEOSGeometry is in a different coordinate system (has a different SRID value) than that of the field, then it will be implicitly transformed into the SRID of the model’s field, using the spatial database’s transform procedure.

And on BaseSpatialField, which is the base of the PointField, the default srid is set to 4326. It warns you that the srid=210 does not exist and proceeds to transform an (x,y) pair to EPSG:4326.

I see two options to work around this (at the moment at least):

  1. The easy way out: Force everything to transform into EPSG:2100 (Greek Grid) by defining it on the model definition:

    class Location(models.Model):
        point = models.PointField(null=True, blank=True, srid=2100)
    
  2. The (sligthly) more complex way: Create a custom exception and a list of accepted SRIDs: SRIDS=[2100, 4326, 3857,...], check the incoming srid against that list and if it doesn't match, raise the custom exception:

    my_app/exceptions.py:

    class UnknownSRID(Exception):
        def __init__(self, message=None):
            self.message = message
    

    my_app/my_test.py:

    from django.conf import settings
    from django.contrib.gis.geos import Point
    
    from testpoint.exceptions import UnknownSRID
    from testpoint.models import Location
    
    some_location = Location()
    test_srid=210
    if test_srid not in settings.SRIDS:
        raise UnknownSRID(
            'SRID {} not in list of acceptable SRIDs'.format(test_srid)
        )
    
    some_location.point = Point(x=15, y=16, srid=test_srid)
    some_location.save()
    
John Moutafis
  • 22,254
  • 11
  • 68
  • 112
  • Thanks. No, actually I wasn't looking for a workaround; I was looking to see whether a real solution exists (i.e. make sqlite behave properly). Let me see what I did in the end... Yep, I'm actually using PostgreSQL, and I only needed this in development, so that the unit tests would actually run; so what I did was skip that test for SQLite. https://github.com/openmeteo/enhydris/blob/f193a3977557ffa33cf267907575f313339b3caf/enhydris/hcore/tests/test_views.py#L1224 – Antonis Christofides Nov 28 '17 at 11:02
  • @AntonisChristofides Well, that is one way to go around that. If you want to be able to use this test though, you can go with the second solution and on the test add: `assertRaises(UnknownSRID,...)` – John Moutafis Nov 28 '17 at 11:57