Reputation: 5190
I'm working with Flask-Marshmallow
for validating request
and response
schemas in Flask app
. I was able to do simple validations for request.form
and request.args
when there are simple fields like Int
, Str
, Float
etc.
I have a case where I need to upload a file using a form field - file_field
. It should contain the file content.
How can I validate if this field is present or not and what is the format of file etc.
Is there any such field in Marshmallow
that I can use like fields.Int()
or fields.Str()
I have gone through the documentation here but haven't found any such field.
Upvotes: 12
Views: 7674
Reputation: 645
As of 7 December, 2022, marshmallow v3.19.0 doesn't work with Raw(metadata={})
or Raw(type='binary/file')
. Hence I tried Field()
and it works fine.
To validate JPG, PNG files, I do that while @validates_schema
by catching it's file type. I have also saved the file after in @post_load
method.
class MySchema(Schema):
icon_url = Field(metadata={'type': 'string', 'format': 'byte'}, allow_none=True)
@validates_schema
def validate_uploaded_file(self, in_data, **kwargs):
errors = {}
file: FileStorage = in_data.get("icon_url", None)
if file is None:
# if any file is not uploaded, skip validation
pass
elif type(file) != FileStorage:
errors["icon_url"] = [
f"Invalid content. Only PNG, JPG/JPEG files accepted"]
raise ValidationError(errors)
elif file.content_type not in {"image/jpeg", "image/png"}:
errors["icon_url"] = [
f"Invalid file_type: {file.content_type}. Only PNG, JPG/JPEG images accepted."]
raise ValidationError(errors)
return in_data
@post_load
def post_load(self, loaded_obj, **kwargs):
if loaded_obj.icon_url:
sec_filename = secure_filename(
f'{loaded_obj.name}.{loaded_obj.icon_url.filename.split(".")[-1]}')
loaded_obj.icon_url.save(
f"{current_app.config['PUBLIC_IMAGES_FOLDER']}{sec_filename}")
loaded_obj.icon_url = f'{current_app.config["PUBLIC_IMAGES_URL"]}{sec_filename}'
return loaded_obj
Upvotes: 2
Reputation: 12762
You can use either Field
or Raw
field to represent a general field, then set the correct OpenAPI properties for this field (i.e. type
and format
).
In OpenAPI 3, you should set type
to string
instead of file
, then set format
to binary
or byte
based on your file content:
from marshmallow import Schema, fields
class MySchema(Schema):
file = fields.Raw(metadata={'type': 'string', 'format': 'binary'})
See the docs for more details.
By the way, from marshmallow 3.10.0, using keyword arguments to pass OpenAPI properties (description
, type
, example
etc.) was deprecated and will be removed in marshmallow 4, use the metadata
dict to pass them instead.
Upvotes: 1
Reputation: 19455
You can use fields.Raw
:
import marshmallow
class CustomSchema(marshmallow.Schema):
file = marshmallow.fields.Raw(type='file')
If you are using Swagger, you would then see something like this:
Then in your view
you can access the file content with flask.request.files
.
For a full example and more advanced topics, check out my project.
Upvotes: 9