A. Scherbaum
A. Scherbaum

Reputation: 855

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

I have a Django 3.1.2 with GIS extension installed, PostgreSQL 12 and PostGIS 3.

In my model I'm trying to use a PointField with geography=True to store real-world coordinates:

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

In the admin menu I'm using the OSMWidget to enter coordinates:

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

When this is saved Django runs into the following SQL error:

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() is used to transform geometry points (which is used as data type if geography=False is used for the PointField). But I'm not sure what's required to make it work with geography.

Upvotes: 0

Views: 643

Answers (1)

user1600649
user1600649

Reputation:

Since you override the PointField it has no srid set and ST_Transform is used to convert the geom to the right spatial reference.

There's several ways to correctly change the widget, but I'll give you the simplest one with your current code:

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

The other ways are:

  • Extend Model.formfield() and change form_class.widget to OSMWidget.
  • Implement ModelAdmin.formfield_overrides and only specify the widget attribute. This works because you do not have to override the entire field, but just specific attributes, the defaults will come from Model.formfield() above, which correctly sets the srid.
  • Override just the widget using the ModelForm's meta class. Works in a similar way as the previous.

Upvotes: 2

Related Questions