flask-admin customise FileUploadField to process only file name and skip file data

63 Views Asked by At

I deploy website on heroku and use flask admin to handle updating db with urls of images and videos. Uploading files to amazon s3 I handle directly with JavaScript. I use JavaScript in order to work around the issue with worker timeout on heroku and allow user to upload files for as long as it takes.

I try to customise FileUploadField from flask_admin.from to not save and not accept any data and only accept file name. My goal with flask admin is only to update data in db and update/delete object from amazon s3 if needed.

# View model for inline Video
class InlineVideoView(InlineFormAdmin):
    form_columns = ["path", "id"]
    
    def prefix_name(obj, file_data):
        parts = op.splitext(file_data.filename)
        new_name = secure_filename(f"{obj}{uuid.uuid4()}{parts[1]}")
        aws_.change_name(file_data.filename, new_name, path='videos/')
        return new_name

    form_extra_fields = {
        'path': CustomFileUploadField(base_path=video_file_path, namegen=prefix_name),
    }

The problem is that I cannot avoid file data processing by CustomFileUploadField and if file is to large it takes a lot of time to update db and hence I got worker time out error.

I tried to change process_formdata method to achieve my goal and in general play around with classes and their attributes/methods from which form.FileUploadInput inherited. But no result so far.

# Customize FileUploadInput to show video while editing
class CustomFileUploadInput(form.FileUploadInput):

    data_template = ('<div>'
                     "<video width='200' height='200' controls>"
                      "<source type='video/mp4' src='https://%(bucket)s.s3.amazonaws.com/videos/%(text)s'></video>"
                     '</div>'
                     '<input %(file)s>')
    
    def process_formdata(self, valuelist):
        if valuelist:
            # Store only the filename, skip the file data
            self.data = valuelist[0].filename
        else:
            self.data = None
    
    def __call__(self, field, **kwargs):
        kwargs.setdefault('id', field.id)
        kwargs.setdefault('name', field.name)

        template = self.data_template if field.data else self.empty_template

        if field.errors:
            template = self.empty_template

        if field.data and isinstance(field.data, FileStorage):
            value = field.data.filename
        else:
            value = field.data or ''

        kwargs['filename'] = kwargs.get('filename', '')
        kwargs['data'] = None

        return Markup(template % {
            'text': value,
            'file': html_params(type='file',
                                value=value,
                                **kwargs),
            'marker': '_%s-delete' % field.name,
            'bucket': os.getenv("S3_BUCKET_NAME")
        })


# Customise FileUploadField to save files to AWS S3
class CustomFileUploadField(form.FileUploadField):

    widget = CustomFileUploadInput()
    
    def _delete_file(self, filename):
        path = self._get_path(filename)
        aws_.s3.delete_object(Bucket=os.getenv("S3_BUCKET_NAME"), Key=f"videos/{filename}")

0

There are 0 best solutions below