Created
October 1, 2012 01:17
-
-
Save goodwillcoding/3808931 to your computer and use it in GitHub Desktop.
JSON Serializable SQLAlchemy Object
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class JsonSerializableMixin(object): | |
""" | |
Converts all the properties of the object into a dict for use in json. | |
You can define the following as your class properties. | |
_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 | |
""" | |
def __json__(self, request): | |
""" | |
Main JSONify method | |
: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 properties | |
if key in blacklist: | |
continue | |
# do not include private and SQLAlchemy properties | |
if 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() | |
continue | |
# get the class property value | |
attr = getattr(self, key) | |
# let see if we need to eagerly load it | |
# this is for SQLAlchemy foreign key fields that | |
# indicate with one-to-many relationships | |
if key in json_eager_load and attr: | |
if hasattr(attr, '_sa_instance_state'): | |
props[key] = self.__try_to_json(request, attr) | |
else: | |
# jsonify all child objects | |
props[key] = [self.__try_to_json(request, x) for x in attr] | |
continue | |
# 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: | |
props[key] = str(attr) | |
except UnicodeEncodeError: | |
props[key] = unicode(attr) # .encode('utf-8') | |
continue | |
props[key] = attr | |
return props | |
def __try_to_json(self, request, attr): | |
""" | |
Try to run __json__ on the given object. | |
Raise TypeError is __json__ is missing | |
:param request: Pyramid Request object | |
:type request: <Request> | |
:param obj: Object to JSONify | |
:type obj: any object that has __json__ method | |
:exception: TypeError | |
""" | |
# check for __json__ method and try to JSONify | |
if hasattr(attr, '__json__'): | |
return attr.__json__(request) | |
# raise error otherwise | |
raise TypeError('__json__ method missing on %s' % str(attr)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment