pythonflaskflask-restfulmarshmallowflask-smorest

flask-smorest response and return type different


I am learning/working on a Rest Api suing flask-smorest and adding the schema using marshmallow.

Below is the code that I am confused with and have a question.

Schemas.py

class ChildAddressDetailsSchema(Schema):
    class Meta:
        unknown = EXCLUDE

    address_id = fields.String(required=True)
    address_type = fields.String(required=True)
    is_primary = fields.Boolean(required=True)


class ChildAddressDetailsSchemaList(Schema):
    class Meta:
        unknown = EXCLUDE

    person_list = fields.List(fields.Nested(ChildAddressDetailsSchema))

Endpoint Implementation

@address_blueprint.response(status_code=200, schema=ChildAddressDetailsSchema)
@address_blueprint.get('/child/address/<string:person_id>/list')
def get_child_address(person_id):
    person_address_list = PersonAddressModel.query.filter_by(person_id=person_id).all()
    person_address_dict = [{'address_id': person_address.address_id,
                            'address_type': person_address.address_type,
                            'is_primary': person_address.is_primary} for person_address in person_address_list]

    return person_address_dict

The part where I have doubt is even though the schema defined in response of blueprint is ChildAddressDetailsSchema which is not a list , still I get a valid response.Below is the screenshot of the Insomnia from where I am testing the api.

enter image description here

I was expecting an empty response or a error since the return of the get function get_child_address is a list of dictionary which is not as per the schema. Could someone please help me figuring out on to how to fix the issue and return type is strictly informed. Is this something that needs to be coded or does marshmallow handles this.


Solution

  • It's because you called Blueprint.response() before Blueprint.get(). So do like this.

    @address_blueprint.get('/child/address/<string:person_id>/list')
    @address_blueprint.response(status_code=200, schema=ChildAddressDetailsSchema)
    def get_child_address(person_id):
        ...
    

    A Python decorator returns a new function that calls the original function. So the order of decorators matters in general. In this case, the implementation of the ResponseMixin.response() of Flask Smorest does not work correctly if the Blueprint.route()(which is equivalent to the Scaffold.get()) is not called before.

    You can see that on this and this. If the ResponseMixin.response() is called before the Blueprint.route(), the closure wrapper(created at the decorator() inside the ResponseMixin.response()) will be ignored, because the add_url_rule() will be called with the original endpoint function not the wrapper, at the decorator() inside the Blueprint.route().