In our Flask app we have several models where instead of storing certain attributes in separate database fields, we prefer to keep these as JSON-serialised objects in a single database field.
Since a JSON data type is only available in some database versions, we had been dealing with this by using the json package to convert json objects into / from strings to store as a text database field. For example:
import json
# eg storing new values
my_attributes = json.dumps({'attr1': 123, 'attr2': 'something'})
my_object.my_attributes = my_attributes
# eg reading values from database
my_attributes = json.loads(my_object.my_attributes)
We finally got sick of doing this :)
Our solution was to introduce a type decorator, which acts as a pre-processor / post-processor when reading / writing from the database for selected field types. Here is what it looks like:
# Create a custom JsonEncodedDict class in a file accessed by your models
import json
from flask.ext.sqlalchemy import SQLAlchemy
from sqlalchemy.ext import mutable
from flask import Flask
app = Flask(__name__, template_folder="../templates")
db = SQLAlchemy(app, session_options={'autocommit': True})
class JsonEncodedDict(db.TypeDecorator):
"""Enables JSON storage by encoding and decoding on the fly."""
impl = db.Text
def process_bind_param(self, value, dialect):
if value is None:
return '{}'
else:
return json.dumps(value)
def process_result_value(self, value, dialect):
if value is None:
return {}
else:
return json.loads(value)
mutable.MutableDict.associate_with(JsonEncodedDict)
Then in your models, use the JsonEncodedDict column type in your model declarations.
class MyObject(db.Model):
__tablename__ = 'my_objects'
object_id = db.Column(db.Integer, primary_key=True)
object_args = db.Column(JsonEncodedDict)
And finally, the JsonEncodedDict type will be used automatically. You can treat the field as a native database JSON field type.
my_obj = MyObject(object_args={'attr1': 123, 'attr2': 'something'})
another_obj = MyObject.query.first()
print another_obj.object_args # eg {'attr1': 123, 'attr2': 'something'}