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

Django PointField with geography=True can't save data: SQL function error

我有一个 Django 3.1.2 安装了 GIS 扩展,PostgreSQL 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: ...", "location") VALUES (15, 10, 'Start', 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,因此它没有设置 srid,并且 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并且只指定widget属性。这是可行的,因为您不必覆盖整个字段,而只需覆盖特定属性,默认值将来自上面的 Model.formfield(),它正确设置了 srid。
  • 使用 ModelForm's meta class 仅覆盖小部件。与之前的工作方式类似。