Skip to content

Instantly share code, notes, and snippets.

@pjenvey
Created October 1, 2012 00:38
Show Gist options
  • Star 14 You must be signed in to star a gist
  • Fork 7 You must be signed in to fork a gist
  • Save pjenvey/3808830 to your computer and use it in GitHub Desktop.
Save pjenvey/3808830 to your computer and use it in GitHub Desktop.
JSON Serializable SQLAlchemy Object
class JsonSerializableMixin(object):
def __json__(self, request):
"""
Converts all the properties of the object into a dict for use in json.
You can define the following in your class
_json_eager_load :
list of which child classes need to be eagerly loaded. This applies
to one-to-many relationships defined in SQLAlchemy classes.
_base_blacklist :
top level blacklist list of which properties not to include in JSON
_json_blacklist :
blacklist list of which properties not to include in JSON
:param request: Pyramid Request object
:type request: <Request>
:return: dictionary ready to be jsonified
:rtype: <dict>
"""
props = {}
# grab the json_eager_load set, if it exists
# use set for easy 'in' lookups
json_eager_load = set(getattr(self, '_json_eager_load', []))
# now load the property if it exists
# (does this issue too many SQL statements?)
for prop in json_eager_load:
getattr(self, prop, None)
# we make a copy because the dict will change if the database
# is updated / flushed
options = self.__dict__.copy()
# setup the blacklist
# use set for easy 'in' lookups
blacklist = set(getattr(self, '_base_blacklist', []))
# extend the base blacklist with the json blacklist
blacklist.update(getattr(self, '_json_blacklist', []))
for key in options:
# skip blacklisted, private and SQLAlchemy properties
if key in blacklist or key.startswith(('__', '_sa_')):
continue
# format and date/datetime/time properties to isoformat
obj = getattr(self, key)
if isinstance(obj, (datetime, date, time)):
props[key] = obj.isoformat()
else:
# get the class property value
attr = getattr(self, key)
# let see if we need to eagerly load it
if key in json_eager_load:
# this is for SQLAlchemy foreign key fields that
# indicate with one-to-many relationships
if not hasattr(attr, 'pk') and attr:
# jsonify all child objects
attr = [x.__json__(request) for x in attr]
else:
# convert all non integer strings to string or if
# string conversion is not possible, convert it to
# Unicode
if attr and not isinstance(attr, (int, float)):
try:
attr = str(attr)
except UnicodeEncodeError:
attr = unicode(attr) # .encode('utf-8')
props[key] = attr
return props
@1xch
Copy link

1xch commented Apr 22, 2013

Why is the request required? Is this a Pyramid thing? I just stumbled on this in a search for looking for a json mixin. I'm using flask and my models don't specifically require the request object, I'm wondering if this is a choice or requirement here, before I try to use it.

@marcofalcioni
Copy link

I think that request is here in case there is a need to inspect the request during serialization. I think you can remove the parameter and it will work just as well. Pyramid has no module-level injection of the request/response objects, so you have to pass them in.

@mattdeboard
Copy link

Sweet, definitely ripping this off minus the request object as I'm not using Pyramid. Appreciate it, especially because it's so well-commented and just generally great-looking code. Thanks!

@kmarekspartz
Copy link

I see other people are "ripping this off". How is this licensed? I'd like to use it, too, plus I have an idea on a @class_method from_json.

@kmarekspartz
Copy link

It was easier to extend JSONEncoder from the standard library.

@jackton1
Copy link

The cost of this loop is very expensive with the conditionals here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment