geography = True的Django PointField无法保存数据:SQL函数错误

问题描述

我有一个安装了GIS扩展名的Django 3.1.2Postgresql 12PostGIS 3

在我的模型中,我尝试将PointFieldgeography=True一起使用来存储实际坐标:

location = models.PointField(geography=True,srid=4326,blank=False,null=False,verbose_name=_("Lat/Lon coordinates"))

在管理菜单中,我正在使用OSMWidget输入坐标:

location = forms.PointField(widget=forms.OSMWidget(attrs={'map_width': 800,'map_height': 500,'default_zoom': 15}))

保存后,Django会遇到以下sql错误

INSERT INTO "challenges_point" ("track_id","sortkey","name","location") VALUES (15,10,'Start',ST_Transform(ST_GeogFromWKB('\x...'::bytea),4326)) RETURNING "challenges_point"."id";
ERROR:  function st_transform(geography,integer) does not exist
LINE 1: ...",ST_Transfo...
                                                   ^
HINT:  No function matches the given name and argument types. You might need to add explicit type casts.

ST_Transform()用于变换几何点(如果geography=False用于PointField,则用作数据类型)。但是我不确定要使其与地理环境一起工作需要什么。

解决方法

由于您覆盖了PointField,所以它没有设置纬线设置,并且使用ST_Transform将geom转换为正确的空间参考。

有几种方法可以正确更改窗口小部件,但是我将用当前代码为您提供最简单的窗口小部件:

location = forms.PointField(
    srid=4326,widget=forms.OSMWidget(
        attrs={'map_width': 800,'map_height': 500,'default_zoom': 15}
    )
)

其他方式是:

  • 扩展Model.formfield()并将form_class.widget更改为OSMWidget。
  • 实施ModelAdmin.formfield_overrides,仅指定窗口小部件属性。之所以可行,是因为您不必覆盖整个字段,而只需覆盖特定的属性,默认值将来自上面的Model.formfield(),它可以正确设置srid。
  • 使用ModelForm's meta class仅覆盖窗口小部件。与以前的工作方式类似。