I created a custom ImageUploadField
in Flask-Admin for uploading images to S3. It's working great, except when I select a single record - the img src
tag now prepends /static/
to all of my file routes and I can't see the image. This is the generated tag from Flask-Admin:
<div class="image-thumbnail"> <img src="/static/https://mybucket.s3.amazonaws.com/mypicture.jpg"> </div><input class="form-control" id="image1" name="image1" type="file"></div>
I also serve static files from my app, so globally changing the static
directory wouldn't be a good idea.
There appears to be several properties on ImageUploadField
that can be modified for this need (endpoint
, url_relative_path
, relative_path
and base_path
) but I was still unable to fix it.
It turns out that the widget
was responsible for generating the URL. It also looks for the image in the static
endpoint (hence why endpoint
defaults to static
when initializing the ImageUploadField
class). To solve this, I created a subclass of this widget and modified the HTML to point directly to the S3 URL, which get's stored on the form.
Although widgets aren't talked about in depth in the Flask-Admin documentation, you can see how they work from pieces of the source code.
Below is the full code for the widget, and part of my custom ImageUploadField
.
class S3ImageUploadInput(ImageUploadInput):
def __call__(self, field, **kwargs):
kwargs.setdefault('id', field.id)
kwargs.setdefault('name', field.name)
args = {
'file': html_params(type='file',
**kwargs),
'marker': '_%s-delete' % field.name
}
if field.data and isinstance(field.data, string_types):
# calling field.data to directly return S3 URL
url = field.data
args['image'] = html_params(src=url)
template = self.data_template
else:
template = self.empty_template
return HTMLString(template % args)
class S3ImageUploadField(ImageUploadField):
widget = S3ImageUploadInput()
def __init__(self, *args, **kwargs):
super(S3ImageUploadField, self).__init__(*args, **kwargs)