1

I'm using Flask version 2.3.2 and FireO ORM 2.1.0

FireO models are not directly compatible with Flask's jsonify. Meaning that they are not serializables by default and need to be converted into a dictionary before passing them to jsonify.

Ex:

users_dicts = [user.to_dict() for user in users]
return jsonify(users_dicts), 200

# or 

return jsonify(user.to_dict()), 200

So my idea was to extend either FireO Models or Flask so I could just do:

jsonify(users), 200  # Users is a FireO QueryIterator
jsonify(user), 200   # User is a FireO model
Caponte
  • 401
  • 1
  • 11
  • 20

2 Answers2

1

I think if you override DefaultJSONProvider.dumps, that function is only called once for the top-level object, so any delegation/recursion has to be done by hand.

I had a much easier time providing a static override for DefaultJSONProvider.default:

class ModelProvider(DefaultJSONProvider):

    @staticmethod
    def default(obj):
        if isinstance(obj, Model):
            return obj.to_dict()
        else:
            return DefaultJSONProvider.default(obj)
chronospoon
  • 510
  • 6
  • 14
0

I achieved the desired behavior by extending Flask's DefaultJSONProvider. But as the majority of the information that I found online was outdated, here is how I did it.

  1. A custom provider extending DefaultJSONProvider. It checks if the object to serialize is one of the relevant FireO classes and converts them into a dictionary or array of dictionaries. For anything else, it defaults back to DefaultJSONProvider behaviour.
from fireo.models import Model
from fireo.queries.query_iterator import QueryIterator
from flask.json.provider import DefaultJSONProvider


class CustomJsonProvider(DefaultJSONProvider):
          
    def dumps(self, obj, **kw):
        """
        Define how to serialize objects that aren't natively serializable by json.dumps.

        Returns:
        - A dictionary if the object is a FireO model
        - A list of dictionaries if the object is a FireO QueryIterator or list of models
        - Datetime objects are serialized to iso strings
        - All other clases are delegated back to DefaultJSONProvider
        """
        
        if isinstance(obj, Model):
            obj = obj.to_dict()
            self.datetime_to_string(obj)

        elif (isinstance(obj, QueryIterator)
            or (isinstance(obj, list) and all(isinstance(item, Model) for item in obj))):
                
            obj = [item.to_dict() for item in obj]
            
            # all datetimes to strings
            for item in obj:
                self.datetime_to_string(item)
                
        return super().dumps(obj, **kw) # Delegate to the default dumps
    
    
    def datetime_to_string(self, obj):
            # Convert all datetimes in the dictionary to string
            
            for key, value in obj.items():
                if isinstance(value, datetime):
                    obj[key] = value.isoformat()
  1. Pass this CustomJsonProvider to Flask's app:
    app = Flask(__name__)
    app.json_provider_class = CustomJsonProvider
    app.json = CustomJsonProvider(app)

P.S: I was hoping to extend FireO's Model classes, but I'm having some difficulties understanding the inner workings of Flask's jsonify and dumps, especially what they require for class serialization.Any advice here would be great.

Caponte
  • 401
  • 1
  • 11
  • 20