Greg Ziegan
Greg Ziegan

Reputation: 5

Serializing Python Arrow objects for the Flask-Restless API

I am currently developing an application with Flask-Restless. When I substituted my SQLAlchemy models' typical DateTime fields with corresponding arrow fields, all went smoothly. This was due to the help of SQLAlchemy-Utils and its ArrowType field.

However, after using the API to return a JSON representation of these objects, I received the following error:

TypeError: Arrow [2015-01-05T01:17:48.074707] is not JSON serializable

Where would be the ideal place to modify how the model gets serialized? Do I modify Flask-Restless code to support Arrow objects or write a model method that Flask-Restless can identify and use to retrieve a JSON-compatible object?

I could also write an ugly post-processor function in the meantime but that solution seems like a terrible hack.

Below is an example model with the ArrowType field:

class Novel(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.Unicode, unique=True, nullable=False)
    created_at = db.Column(ArrowType, nullable=False)

    def __init__(self, title):
        self.title = title
        self.created_at = arrow.utcnow()

Upvotes: 0

Views: 3120

Answers (2)

Ali Cirik
Ali Cirik

Reputation: 1572

Arrow now has a for_json method. For example: arrow.utcnow().for_json()

Upvotes: 8

bsa
bsa

Reputation: 2801

How about a custom JSONEncoder which supports Arrow types? Looking at the Flask-Restless source code, it uses Flask's built in jsonify under the hood. See this snippet for an example which serializes regular datetime objects in a different format: http://flask.pocoo.org/snippets/119/

Here's a full self-contained example for good measure:

import flask
import flask.ext.sqlalchemy
import flask.ext.restless
from flask.json import JSONEncoder
import sqlalchemy_utils
import arrow

app = flask.Flask(__name__)
app.config['DEBUG'] = True
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///test.db'
db = flask.ext.sqlalchemy.SQLAlchemy(app)

class Event(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    timestamp = db.Column(sqlalchemy_utils.ArrowType)

class ArrowJSONEncoder(JSONEncoder):
    def default(self, obj):
        try:
            if isinstance(obj, arrow.Arrow):
                return obj.format('YYYY-MM-DD HH:mm:ss ZZ')
            iterable = iter(obj)
        except TypeError:
            pass
        else:
            return list(iterable)
        return JSONEncoder.default(self, obj)

app.json_encoder = ArrowJSONEncoder

db.create_all()
manager = flask.ext.restless.APIManager(app, flask_sqlalchemy_db=db)
manager.create_api(Event, methods=['GET','POST'])

app.run()

From the two options in your post, I'd suggest adding the method to your model class to retrieve a JSON-compatible object, only because it's simpler and more maintainable. If you want to modify Flask-Restless, you either need to fork it or monkey patch it.

Upvotes: 3

Related Questions