iommirocks / iommi

Your first pick for a django power cord
http://iommi.rocks
BSD 3-Clause "New" or "Revised" License
718 stars 47 forks source link

Form.create() with FileField - double save problem #419

Closed berycz closed 1 year ago

berycz commented 1 year ago

In create form when I tried to upload files bigger than settings.FILE_UPLOAD_MAX_MEMORY_SIZE, I got FileNotFoundError(No such file or directory: '/tmp/tmp9vcn09ii.upload.jpg')

After some digging the problem is in form.create_or_edit_object__post_handler the double-save for is_create, the second save is saving the file again. If the file is smaller than settings.FILE_UPLOAD_MAX_MEMORY_SIZE, it gets saved twice (eg uyuni.jpeg and uyuni_KzLJV52.jpeg). If the file is bigger, the first save copies the file and removes the /tmp/ file, so the second save raises an exception in django/core/files/move.py L61: with open(old_file_name, "rb") as old_file:

I suppose you need to skip FileField / ImageField in the

        for field in values(form.fields):
            # two phase save for creation in django, have to save main object before related stuff
            if not field.extra.get('django_related_field', False):
                form.apply_field(field=field, instance=form.instance)

or in form.apply(form.instance), but only for is_create

EDIT: since it's hard to tell where to skip it and also there might be some custom model fields that will need it, how about something like:

    @classmethod
    @with_defaults(
        input__attrs__type='file',
        raw_data=file__raw_data,
        write_to_instance=file_write_to_instance,
        skip_in_first_save_on_create=False,
        skip_in_second_save_on_create=True,
    )
    def file(cls, **kwargs):
        return cls(**kwargs)

then if you skip it in first save, you can use obj.pk in upload_to, which is very useful imo 🤔

boxed commented 1 year ago

Maybe a safe_phase or save_method enum that could have some reasonable semantic values that we interpret differently for save/create depending on what makes sense?