Flask: Converting Python dict to json object for client api

10,601

Generally is a bad idea your approach on jsonifying a model:

  • self.__dict__ may contain a lot of undocumented keys
  • column types: you can't jsonify relationship and column types directly. And notably a datetime column.
  • security: sometimes you might want to hide some fields (for untrusted users consuming your APIs for example)

A good approach is to create a method to return a json serializable dictionary:

class Foo(db.Model):
    field1 = db.Column(...)
    field2 = db.Column(...)

    def as_dict(self):
        obj_d = {
            'field1': self.field1,
            'field2': self.field2,
            ...
        }
        return obj_d

Then in your view:

foos = Foo.query.all()
results = [ foo.as_dict() for foo in foos ]

return jsonify({count: len(results), results: results)

Based on your application you can make as_dict smarter by converting fields (notably datetime fileds) in javascript friendly formats or adding convenient fields like following a relationship.

Share:
10,601
user2957824
Author by

user2957824

Updated on June 05, 2022

Comments

  • user2957824
    user2957824 almost 2 years

    I need to create a json object from a flask a query result. I then need to pass the json object to the route to create an API.

    In searching for some slick ways to create dicts from my instances I stumbled upon a method in this post to use the internal dict of the instance and add a jsond method to the model class. Here is the Model with the custom method, 'jsond':

    from app import db  
    class Rest(db.Model):
        id = db.Column(db.Integer, primary_key = True)
        name = db.Column(db.String(100), unique = True)
        street = db.Column(db.Text)
        zipcd = db.Column(db.Integer)
        comments = db.relationship('Comment', backref='rest', lazy='dynamic')
        lat = db.Column(db.Float(6))
        lng = db.Column(db.Float(6))
    
        def __init__(self,name,street,zipcd):
            self.name = name
            self.street = street
            self.zipcd = zipcd
    
        def __repr__(self):
            return '{}'.format(self.name)
    
        def name_slug(self):
            return self.name
    
        def jsond(self):
            instDict = self.__dict__.copy()
            if instDict.has_key('_sa_instance_state'):
                del instDict['_sa_instance_state']
            return instDict
    

    And here's my view function:

    from app import app, db
    from flask import render_template, flash, redirect, session, url_for, request, g,      jsonify, make_response
    from flask.json import dumps
    from flask.ext import restful 
    from flask.ext.httpauth import HTTPBasicAuth
    from models import Comment, Rest, Badge
    from helper import make_badges, make_inspections, loc_query
    import operator
    auth = HTTPBasicAuth()
    
    @app.route('/api',methods=['GET'])
    def makeApi():
    
        ###Query Parameters###
        lim = request.args.get('limit', 10)
        off = request.args.get('offset', 0)
        loc = request.args.get('location', "39.94106,-75.173192")
        lat, lng = loc.split(",")
        radius = request.args.get('radius',2)
    
        query = loc_query(lat,lng,radius,off,lim)
    
        results = Rest.query.from_statement(query).all()
    
    
        rest_json = []
        for rest in results:
            rest_json.append(rest.jsond())
    
        return make_response(jsonify({'count':len(rest_json),'rests':rest_json}))
    

    So when in the python API command line I can run queries successfully and create a dict from a particular instances in a query with all fields present (using the custom jsond method). However when I go to the makeApi route using my view I get a json object with ONLY THE 'id' field present:

    rests: [
    {
    id: 28450
    },
    {
    id: 28795
    },
    {
    id: 30439
    },
    {
    id: 29325
    },
    {
    id: 29765
    },
    {
    id: 29928
    },
    {
    id: 30383
    },
    {
    id: 29064
    },
    {
    id: 29862
    },
    {
    id: 28610
    }
    ]
    }
    

    I've been going in circles for hours and have no idea why the view behavior would differ from the python API. Perhaps its something Im doing wrong with jsonify but i dont think so.

  • user2957824
    user2957824 about 10 years
    @paolocasiello is there way to pass a dict object for use in the template directly? if so is this ever beneficial?
  • Paolo Casciello
    Paolo Casciello about 10 years
    You mean Jinja templates? You can pass the whole model to the templates render_template(..., mod=model) and use functions/attributes directly from the template
  • qazimusab
    qazimusab over 7 years
    Thanks so much! This helped me a ton!