jrief / django-formset

The missing widgets and form manipulation library for Django
https://django-formset.fly.dev/
MIT License
317 stars 30 forks source link

UploadedFileInput Error when modifying or deleting model #151

Open jamiecash opened 2 months ago

jamiecash commented 2 months ago

When using the UploadedFileInput.On creation, uploaded image is stored successfully, however when modifying the model but not changing the image, or when deleting the model, the following validation error is shown: No file was submitted. Check the encoding type on the form.

The form

class SubjectForm(forms.ModelForm):
    """
    Form to create and edit lesson subjects.
    """

    name = fields.CharField(max_length=100, required=True)
    image = fields.ImageField(
        label="Image",
        widget=UploadedFileInput(attrs={
            'max-size': 1024 * 1024,
        }),
        help_text="Files larger than 1MB are not supported.",
        required=False,
    )

    class Meta:
        model = models.Subject
        fields = ['name', 'image']

The model

class Subject(models.Model):
    """
    A subject being taught.
    """
    name = models.CharField(max_length=100)
    image = models.ImageField(upload_to='subjects/images/', blank=True, null=True)

    def __str__(self):
      return self.name

    class Meta:
      db_table = 'lessons_subject'
      ordering = ('name',)

When debugging, the FormViews form_valid method is not reached.

jamiecash commented 2 months ago

When modifying the model and changing the image, the error does not occur. This appears to be an issue when editing the model but not adding or changing the image.

jrief commented 1 month ago

Here is an example implementing this: https://django-formset.fly.dev/bootstrap/person (it's contained in the supplied demos of this project).

Maybe I didn't understand your question.

jamiecash commented 1 month ago

The test app includes the create workflow for creating a Person with an image. The issue occurs on the update and delete workflows. update only works if you reattach the image, submitting with the existing image raises the No file was submitted. Check the encoding type on the form error. delete also does the same.

In summary, the error occurs when a model is loaded with an image and a form is displayed with the model instance, the form is updated but the image or file field is left unchanged, and then the form is submitted. The error occurs in validation, prior to the _formvalid method being called

jamiecash commented 1 month ago

After a lot of debugging.

To stop this from occurring, if you are loading and updating an Image field, you must use the FileField field from formset.fields. This has an overridden clean method. If you use the default field for models.FileField or models.ImageField then the field is incorrectly marked as changed in the form, which causes the error.

Example

from formset.fields import FileField

class SubjectForm(forms.ModelForm):
    image = FileField()

    class Meta:
        model = models.Subject
        fields = ['name', 'image']

Note, you don't have to specify the widget as this is specified in FileField.

This may require an update to the documentation to make it clearer.

jrief commented 6 days ago

Now I see what might be the problem. Does your SubjectForm also inherit from formset.utils.FormMixin. This mixin is required to patch some methods. Especially the Django FileField must be handled in a different way, check here for details: https://github.com/jrief/django-formset/blob/e72e58ce8446b1299fb3987879463e573bd66400/formset/utils.py#L168