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}")