python-restx / flask-restx

Fork of Flask-RESTPlus: Fully featured framework for fast, easy and documented API development with Flask
https://flask-restx.readthedocs.io/en/latest/
Other
2.16k stars 335 forks source link

Allow multiple marshal_with decorators #347

Open MajorDallas opened 3 years ago

MajorDallas commented 3 years ago

Is your feature request related to a problem? Please describe. Using @namespace.marshal_with multiple times on one method results in only the outer-most marhal_with's model being used. The only workaround is to instead use @namespace.response and explicitly call marshal within a mess of control flow statements.

Describe the solution you'd like I would like to get rid of the many if-blocks and replace them with marshal_with decorators that can dispatch to the correct model based on status code.

# instead of this:
    @ns.response(200, ok_model)
    @ns.response(400, validation_err)
    @ns.response(500, internal_err)
    def post(self):
        result = do_stuff_on(request.json)
        if (status := result[1]) == 200:
            return marshal(result.content, ok_model), result[1]
        elif status == 500:
            return marshal(result.content, internal_err), result[1]
        elif status == 400:
            return marshal(result.content, validation_err), result[1]
        # This gets REAL messy if you can get any of a dozen different outcomes that all need 
        # distinct models, or if the result state is communicated in a more rich/complex way 
        # or not at all.

# this:
    @ns.marshal_with(ok_model, code=200)
    @ns.marshal_with(validation_err, code=400)
    @ns.marsnal_with(internal_err, code=500)
    def post(self):
        return do_stuff_on(request.json)

# or even this:
    @ns.marshal_with({200: ok_model, 400: validation_err, 500: internal_err})
    def post(self):
        return do_stuff_on(request.json)

I don't think the current closure-based approach can be easily hacked to allow this. My first thought was to use a mapping of model names to marshalling.marshal_with instances, but I'm not sure how to change what the closure returns or how the handler then gets called. Second thought was singledispatch, but since that can't dispatch on Literal types as of Py38 you'd need a class for every HTTP status code... (or a class factory).

Enno-H commented 3 years ago

We definitely need this feature.

yanivakiva commented 3 years ago

this is a MUST feature

mhetreramesh commented 1 year ago

Is there any solution or workaround to this in 2023?

nick4fake commented 1 month ago

So basically the most trivial example is not supported? We can't use marshal when there are multiple possible http codes, so... always?