Search code examples
pythonflasksqlalchemymarshmallow

Make Flask Marshmallow NestedSchema work wtth my API route


I'm making an APi using Flask, SQLAlchemy, and Marshmallow.

The model is defined and the database is populated with data and when the view is hit. I get all the fields JSON content from NonProfitSchema.

class NonprofitSchema(ma.ModelSchema):
    class Meta:
        model = Nonprofit

        # To specialize fields something like so
        fields = ('id', 'name', 'ein', 'ico', 'street', 'city', 'state', 'zipcode', 'group', 'subsection', 'affiliation', 'classification', 'ruling',
 'deductability', 'foundation', 'activity', 'organization', 'status', 'tax_period', 'asset_cd','income_cd', 'filing_req_cd', 'pf_filing_req_cd',
 'acct_pd', 'asset_amt', 'income_amt', 'revenue_amt', 'ntee', 'sort_name', 'activity_full')

I was trying to make a new Schema from this and return only a subset of the data with so below

class AddressSchema(NonProfitSchema):

    from marshmallow import fields 
    address = fields.Nested(NonprofitSchema(only=("street", "city", "state", "zipcode",)))  

I had routes in my view.py set up as so:

@api_blueprint.route("/api/orgs/id/<int:id>/", methods=["GET"]) def get_org_by_id(id):
    org = Nonprofit.query.get(id)
    return npschema.jsonify(org)

@api_blueprint.route("/api/orgs/address/<int:id>/", methods=["GET"]) def get_org_by_id_address(id):
    org = Nonprofit.query.get(id)
    return addyschema.jsonify(org)

However, when accessing them in a browser. They both return the exact same thing which is the entire database model of NonprofitSchema

I'm supposing it's not possible to do subsets of data with the Schema, right?


Solution

  • I should update the answer that I went with. While I and user c8999c 3f964f64 basically thought of the same way and that will indeed sort of work. In my case, "jsonify" on the object wouldnt work as in his example I built a separate function as there is no items attribute. So with a filter made that uses get attribute as so:

    def jsonfilter(obj, attrlist):
    
        resp = {}
        for attr in attrlist:
            resp[attr] = getattr(obj,attr)
        return resp
    

    Then modifying the function as so to use it:

    @api_blueprint.route("/api/orgs/id/<int:id>/address", methods=["GET"])
    def get_org_address_by_id(id):
        org = Nonprofit.query.get(id)
        only_these_fields = ["id", "ein", "name", "street", "city", "state", "zipcode"]
        return jsonify(jsonfilter(org, only_these_fields))
    

    I was able to make it work.

    Alternatively and a much simpler way after reviewing the Flask and Flask-Marshmallow documentation is to simply instantiate a new class object of the model with fields passed in.

    @api_blueprint.route("/api/orgs/id/<int:id>/address", methods=["GET"])
    def get_org_address_by_id(id):
        org = Nonprofit.query.get(id)
        only_these_fields = ["id", "ein", "name", "street", "city", "state", "zipcode"]
        addyschema = NonprofitSchema(fields=only_these_fields)
        return addyschema.jsonify(org)
    

    I opted against this method because I prefer to only need one separate class object and calling a filter method feels better.