diff --git a/django/contrib/gis/db/models/fields.py b/django/contrib/gis/db/models/fields.py index 7519b30fb2f..4ded5b1b4e5 100644 --- a/django/contrib/gis/db/models/fields.py +++ b/django/contrib/gis/db/models/fields.py @@ -312,6 +312,12 @@ class GeometryField(GeoSelectFormatMixin, BaseSpatialField): """ return connection.ops.get_distance(self, value, lookup_type) + def get_db_prep_value(self, value, connection, *args, **kwargs): + return connection.ops.Adapter( + super(GeometryField, self).get_db_prep_value(value, connection, *args, **kwargs), + **({'geography': True} if self.geography else {}) + ) + def from_db_value(self, value, expression, connection, context): if value: if not isinstance(value, Geometry): diff --git a/django/db/models/expressions.py b/django/db/models/expressions.py index ce685203eac..6bfbc365341 100644 --- a/django/db/models/expressions.py +++ b/django/db/models/expressions.py @@ -579,6 +579,8 @@ class Value(Expression): val = self.output_field.get_db_prep_save(val, connection=connection) else: val = self.output_field.get_db_prep_value(val, connection=connection) + if hasattr(self._output_field, 'get_placeholder'): + return self._output_field.get_placeholder(val, compiler, connection), [val] if val is None: # cx_Oracle does not always convert None to the appropriate # NULL type (like in case expressions using numbers), so we diff --git a/tests/gis_tests/geoapp/test_expressions.py b/tests/gis_tests/geoapp/test_expressions.py new file mode 100644 index 00000000000..c18d07f0e80 --- /dev/null +++ b/tests/gis_tests/geoapp/test_expressions.py @@ -0,0 +1,31 @@ +from unittest import skipUnless + +from django.contrib.gis.db.models import GeometryField, Value, functions +from django.contrib.gis.geos import Point, Polygon +from django.test import TestCase, skipUnlessDBFeature + +from ..utils import postgis +from .models import City + + +@skipUnlessDBFeature('gis_enabled') +class GeoExpressionsTests(TestCase): + fixtures = ['initial'] + + def test_geometry_value_annotation(self): + p = Point(1, 1, srid=4326) + point = City.objects.annotate(p=Value(p, GeometryField(srid=4326))).first().p + self.assertEqual(point, p) + + @skipUnlessDBFeature('supports_transform') + def test_geometry_value_annotation_different_srid(self): + p = Point(1, 1, srid=32140) + point = City.objects.annotate(p=Value(p, GeometryField(srid=4326))).first().p + self.assertTrue(point.equals_exact(p.transform(4326, clone=True), 10 ** -5)) + self.assertEqual(point.srid, 4326) + + @skipUnless(postgis, 'Only postgis has geography fields.') + def test_geography_value(self): + p = Polygon(((1, 1), (1, 2), (2, 2), (2, 1), (1, 1))) + area = City.objects.annotate(a=functions.Area(Value(p, GeometryField(srid=4326, geography=True)))).first().a + self.assertAlmostEqual(area.sq_km, 12305.1, 0)