Pegase745 / sqlalchemy-datatables

SQLAlchemy integration of jQuery DataTables >= 1.10.x (Pyramid and Flask examples)
MIT License
159 stars 67 forks source link

enums are not json serializable #128

Closed eddieparker closed 4 years ago

eddieparker commented 4 years ago

I have a model like this:

class Host(Model):
       # ... snip ...
       provision_state             = Column(Enum(ProvisionStateEnum), default=ProvisionStateEnum.unknown)

If I use it with ColumnDT, I get an error when rendering:

TypeError
TypeError: Object of type 'ProvisionStateEnum' is not JSON serializable

Is there a way to support enums?

eddieparker commented 4 years ago

I figured it out - posting my solution here in case others need it.

I didn't realize that the error was actually coming from flask, not sqlalchemy-datatables, so I simply had to build a custom JSON encoder. This was taken from a flask gist somewhere and modified:

class CustomJSONEncoder(JSONEncoder):

    def default(self, obj):
        try:
            if isinstance(obj, datetime):
                if obj.utcoffset() is not None:
                    obj = obj - obj.utcoffset()
                millis = int(
                    calendar.timegm(obj.timetuple()) * 1000 +
                    obj.microsecond / 1000
                )
                return millis
            iterable = iter(obj)
        except TypeError:
            pass
        else:
            return list(iterable)

        try:
            return JSONEncoder.default(self, obj)
        except TypeError as type_error:

            serialize_func = getattr(obj, 'json_encode', None)

            if serialize_func is not None:
                return serialize_func(self)
            else:
                raise

appbuilder.app.json_encoder = CustomJSONEncoder

(substitutde appbuilder.app for app, or a blueprint if you're using that with flask).

The code above allows a class to provide a 'json_encode' function that will be called if all else fails, like this:

class ProvisionStateEnum(enum.Enum):
    foo = 1

    def json_encode(self, encoder):
        return self.value
cjauvin commented 3 years ago

Thank you that is very useful to me right now.