flask-smorest icon indicating copy to clipboard operation
flask-smorest copied to clipboard

Dynamic serialization schema

Open BnGx opened this issue 4 years ago • 3 comments

It would be useful to reimplement the ResponseMixin.response function to accept a static function as the schema argument so that we can build the schema based on the client request, which is needed for JSON:API requests and using marshmallow-jsonapi as a serializer.

Below is an example of a use case:


@bp.route('/')
class Resource(MethodView)

    def get_fields(request):
        fields = parser.parse(JsonApiRequestSchema, request, location='query')
        schema = ResourceSchema(many=true, only=fields)
        return schema

    @bp.response(200, get_fields)
    def get(self):
        pass

BnGx avatar Feb 17 '21 21:02 BnGx

What should the generated doc look like?

lafrech avatar Feb 17 '21 22:02 lafrech

I suppose it is necessary to represent a static schema in the generated document. So the document should appear using the passed Marshmallow schema in the response function. Instead serialization should happen based on same schema but using different arguments. To resolve this problem you could define two arguments in the response function only and exclude and evaluate them in the decorator inside response.

The following code is an example to integrate a different serialization logic in your project. It can be useful to use JSON:API spec with OpenAPI doc.

from flask import request

class ResponseMixin:
    def response(
            self, status_code, schema=None, *, description=None,
            example=None, examples=None, headers=None,
            only=None, exclude=()
    ):

        # ...

        def decorator(func):

            @wraps(func)
            def wrapper(*args, **kwargs):
                if schema:
                    if only:
                        schema.only = only(request) if callable(only) else set(only)
                    if exclude:
                        schema.exclude = exclude(request) if callable(exclude) else set(exclude)
                    schema._init_fields()

                 # ...

This link helps to render what I mean.

BnGx avatar Feb 18 '21 13:02 BnGx

After a series of tests and evaluations I think it is more appropriate to use a dedicated serialization_schema to give more flexibility to the code and avoid interacting with the internal logic of the schema.

​from​ ​flask​ ​import​ ​request​

​class​ ​ResponseMixin​:
    ​def​ ​response​(
            ​self​, ​status_code​, ​schema​=​None​, *, ​description​=​None​,
            ​example​=​None​, ​examples​=​None​, ​headers​=​None​,
            serialization_schema=None
    ):

        ​# ...​

        ​def​ ​decorator​(​func​):

            ​@​wraps​(​func​)​
            ​def​ ​wrapper​(​*​args​, ​**​kwargs​):
                ​if​ ​serialization_schema:
                    schema​ ​=​ ​serialization_schema​(​request​) ​if​ ​callable​(​serialization_schema​) ​else​ ​serialization_schema

                 ​# ...

BnGx avatar Feb 23 '21 23:02 BnGx