I’ll be talking about django.contrib.gis.db.models.functions.Perimeter and the as_postgresql method
First case with GeneratedField
from django.contrib.gis.db.models.functions import GeoFunc
from django.db import models
from django.contrib.gis.db.models import PolygonField
class Geography(GeoFunc):
function = 'geography'
class Crosswalk(models.Model):
polygon = PolygonField()
perimeter = models.GeneratedField(
expression=Perimeter(Geography('polygon')),
output_field=models.FloatField(),
db_persist=True,
)
When i try to migrate i get this error
django.db.utils.NotSupportedError: ST_Perimeter cannot use a non-projected non-geography field.
Second case with annotate and without Geography
from django.db import models
from django.contrib.gis.db.models import PolygonField
class Crosswalk(models.Model):
polygon = PolygonField()
Crosswalk.objects.annotate(p=Perimeter('polygon'))
And on this case i too get this error
django.db.utils.NotSupportedError: ST_Perimeter cannot use a non-projected non-geography field.
But if I make a custom Perimeter function and remove this check, everything works correctly and I get the result.
class P(Perimeter):
function = 'st_perimeter'
def as_postgresql(self, compiler, connection, **extra_context):
function = None
dim = min(f.dim for f in self.get_source_fields())
if dim > 2:
function = connection.ops.perimeter3d
return super().as_sql(compiler, connection, function=function, **extra_context)
Source: django/django/contrib/gis/db/models/functions.py at main · django/django · GitHub
Why does it work that way? Is it a bug?