Search code examples
djangodjango-modelsdjango-formsdjango-admingeodjango

Django: Point not passing from ModelForm clean to Model clean


I have an Occurrence model that I am putting as TabularInline on the admin page of another model. It has a PointField and a PolygonField, of which at least one must exist.

Here is part of the model:

class Occurrence(models.Model):
    name = models.CharField(max_length=254)
    location = models.PointField(null=True, blank=True)
    bounding_polygon = models.PolygonField(null=True, blank=True)

    def clean(self):
        # Checks if at least one of the spatial fields is filled in
        if not self.location and not self.bounding_polygon:
            raise ValidationError('At least one of the spatial fields (point or polygon) is required.')

In the TabularInline I wanted to put the name and location but when I add new rows the map of the location does not appear. To get around this problem, I used a form where you can enter the latitude and longitude, which is then converted to the Point of location.

Here is my form:

class OccurrenceForm(forms.ModelForm):
    latitude = forms.FloatField(
        min_value=-90,
        max_value=90,
        required=False,
        help_text="Enter coordinates as an alternative to selecting a point on the map."
    )
    longitude = forms.FloatField(
        min_value=-180,
        max_value=180,
        required=False,
    )

    class Meta(object):
        model = Occurrence
        exclude = []

    def __init__(self, *args, **kwargs):
        super(OccurrenceForm, self).__init__(*args, **kwargs)
        coordinates = self.initial.get("location", None)
        if isinstance(coordinates, Point):
            self.initial["longitude"], self.initial["latitude"] = coordinates.tuple

    def clean(self):
        data = super(OccurrenceForm, self).clean()
        #if "latitude" in self.changed_data or "longitude" in self.changed_data:
        lat, lng = data.pop("latitude", None), data.pop("longitude", None)
        if lat and lng:
            data["location"] = Point(lng, lat, srid=4326)
        return data

Here is my TabularInline model:

class OccurrencesInline(admin.TabularInline):
    model = Occurrence
    fields = ('name', 'latitude', 'longitude')
    show_change_link = True
    extra = 1
    form = OccurrenceForm

However, the Point created for the location in the ModelForm is not passed to the Model clean. I have tested this same ModelForm without being in a TabularInline and it works without problems.

Does anyone have any idea why this happens?


Solution

  • I will post the solution, since it might be useful for someone.


    The problem was that I did not put the PointField that I have defined in the model into the fields defined in OccurrencesInline.

    The solution was to add the field, but as I didn't want it visible I hid it using forms.HiddenInput.

    class OccurrencesInline(admin.TabularInline):
        model = Occurrence
        fields = ('name', 'latitude', 'longitude', 'location')
        show_change_link = True
        extra = 1
        formset = AtLeastOneFormSet
        form = OccurrenceForm
        formfield_overrides = {
            models.PointField: {'widget': forms.HiddenInput()},
        }