apryor6 / flask_accepts

Easy, opinionated Flask input/output handling mixing Marshmallow with flask-restx
BSD 3-Clause "New" or "Revised" License
169 stars 40 forks source link

What is the best practice for file uploads? #84

Open datavistics opened 4 years ago

datavistics commented 4 years ago

One of the endpoints Im writing is based on file upload. What is the best way to use the accepts decorator for this?

volfpeter commented 3 years ago

File upload is probably out of scope for flask_accepts, since its goal is to make marshmallow and flask_restx play together. I think the best solution is to write your own accepts_file() decorator on top of flask_restx (example here).

volfpeter commented 3 years ago

Example solution:

from functools import wraps

from flask import abort
from flask_restx import Namespace
from flask_restx.reqparse import RequestParser
from werkzeug.datastructures import FileStorage

def accepts_files(*, api: Namespace, argument_name: str = "files", required: bool = True):
    """
    Decorator that adds a file upload entry to the swagger documentation.

    The decorated method must accept an `argument_name` keyword argument,
    whose value will be the list of files (more precisely `werkzeug.datastructures.FileStorage`
    objects) that were sent by the client in the `argument_name` form part.
    """

    file_upload_parser = RequestParser()
    file_upload_parser.add_argument(name=argument_name, type=FileStorage, location="files", action="append", required=required)

    def decorator(func):
        @wraps(func)
        def inner(*args, **kwargs):
            files = None
            try:
                data = file_upload_parser.parse_args()
                files = data[argument_name]
            except Exception:
                pass

            if files is None and required:
                abort(400)

            return func(*args, **kwargs, **{argument_name: files})

        return api.expect(file_upload_parser)(inner)

    return decorator