How to Serialize Flask SQLAlchemy to JSON

Recently I was involved in developing a PPE response network built by The Luminosity Lab at Arizona State University. Its backend was built using Flask. This was my first time working on a Flask application and I was surprised that it is not trivial to elegantly serialize the database model classes to JSON. There are multiple approaches that can be used to serialize the classes. In this post, I will introduce you to the approach that I liked the most.

Using SQLAlchemy-serializer

The SQLAlchemy-serializer library makes serializing objects to JSON quite easy and it has got quite a few customizations that let you handle all of your custom use cases.

n0nSmoker/SQLAlchemy-serializer

Install the library

Install the library using pip.

pip install SQLAlchemy-serializer

Define the Model Class

Define your database’s model class using SQLAlchemy.

class Login(db.Model, CustomSerializerMixin):
    __tablename__ = 'login'

    serialize_only = ('id', 'email_id', 'role_type', 'users.id')
    
    serialize_rules = ('-merchants')

    id = db.Column(UUID(as_uuid=True), primary_key=True, unique=True)
    email_id = db.Column(db.String(120), unique=True, nullable=False)
    password = db.Column(db.String(200), nullable=False)
    role_type = db.Column(db.Enum(Role))
    users = db.relationship('Users', backref='login', uselist=False)
    merchants = db.relationship('Merchants', backref='login', uselist=False)

Define a Custom Serializer

If your database table uses classes like UUID , Geometry etc., you will need to define a custom serializer for it.

class CustomSerializerMixin(SerializerMixin):
    serialize_types = (
        (UUID, lambda x: str(x)),
    )

Check out the documentation to see what data types are natively supported by the library.

Define fields to serialize

You can specify the list of fields you want to serialize by using the serialize_only field in your model class.

serialize_only = ('id', 'email_id', 'role_type', 'users.id')

Note: In this example, we have a users relationship defined and we want to serialize just the id field of users.

Define fields to exclude

You can exclude the whole relationship object by using a minus(-) symbol before the relationship name.

serialize_rules = ('-merchants')

In this example, we want to exclude all the fields of the merchants field when serializing the JSON.

Avoid circular dependency

If you have a relationship and foreign key defined in your models, you could run into a circular dependency issue. Suppose you have the following model classes defined:

class Login(db.Model, CustomSerializerMixin):
  __tablename__ = 'login'

  serialize_only = ('id', 'email_id', 'role_type', 'users')

  id = db.Column(UUID(as_uuid=True), primary_key=True, unique=True)
  email_id = db.Column(db.String(120), unique=True, nullable=False)
  password = db.Column(db.String(200), nullable=False)
  role_type = db.Column(db.Enum(Role))
  users = db.relationship('Users', backref='login', uselist=False)
  merchants = db.relationship('Merchants', backref='login', uselist=False)
    
class Users(db.Model, CustomSerializerMixin):
  __tablename__ = 'users'

  id = db.Column(UUID(as_uuid=True), primary_key=True)
  first_name = db.Column(db.String(120), nullable=False)
  middle_name = db.Column(db.String(120), nullable=True)
  last_name = db.Column(db.String(120), nullable=False)
  phone_extension = db.Column(db.String(10), nullable=False)
  phone_number = db.Column(db.String(20), nullable=False)
  login_id = db.Column(UUID(as_uuid=True), db.ForeignKey('login.id'), nullable=False, unique=True)

Notice that the login class has users added in its serialize_only field. With this setup you will face a maximum recursion depth exceeded exception. The serializer will find an instance of the same class among the model’s relationships. To avoid this you need to define where the serialization should stop:

serialize_rules = ('-users.login.users',)

Serialize JSON

With the serializer mixin in place, its very simple to serialize your object to JSON.

login.to_dict()

You can buy me a coffee if this post really helped you learn something or fix a nagging issue!


Written on May 10, 2020 by Vivek Maskara.

Originally published on Medium

Vivek Maskara
Vivek Maskara
SDE @ Remitly

SDE @ Remitly | Graduated from MS CS @ ASU | Ex-Morgan, Amazon, Zeta

Related