Created
October 1, 2012 00:38
-
-
Save pjenvey/3808830 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): | |
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 |
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.
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!
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.
It was easier to extend JSONEncoder from the standard library.
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
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.