3

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.

2
  • What's wrong with "dumps({...})", which you already imported? Commented May 2, 2014 at 3:59
  • @swstephe dumps is giving me the same error - ony the 'id' field shows up Commented May 2, 2014 at 4:05

1 Answer 1

4

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.

Sign up to request clarification or add additional context in comments.

2 Comments

@paolocasiello is there way to pass a dict object for use in the template directly? if so is this ever beneficial?
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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.