Serialchemy was developed as a module of Flask-RESTAlchemy, a lib to create Restful APIs using Flask and SQLAlchemy. We first tried marshmallow-sqlalchemy, probably the most well-known lib for SQLAlchemy model serialization, but we faced issues related to nested models. We also think that is possible to build a simpler and more maintainable solution by having SQLAlchemy in mind from the ground up, as opposed to marshmallow-sqlalchemy that had to be designed and built on top of marshmallow.
Suppose we have an Employee SQLAlchemy model declared:
class Employee(Base):
__tablename__ = "Employee"
id = Column(Integer, primary_key=True)
fullname = Column(String)
admission = Column(DateTime, default=datetime(2000, 1, 1))
company_id = Column(ForeignKey("Company.id"))
company = relationship(Company)
company_name = column_property(
select([Company.name]).where(Company.id == company_id)
)
password = Column(String)
Generic Types are automatically serialized by ModelSerializer:
from serialchemy import ModelSerializer
emp = Employee(fullname="Roberto Silva", admission=datetime(2019, 4, 2))
serializer = ModelSerializer(Employee)
serializer.dump(emp)
# >>
{
"id": None,
"fullname": "Roberto Silva",
"admission": "2019-04-02T00:00:00",
"company_id": None,
"company_name": None,
"password": None,
}
New items can be deserialized by the same serializer:
new_employee = {"fullname": "Jobson Gomes", "admission": "2018-02-03"}
serializer.load(new_employee)
# >> <Employee object at 0x000001C119DE3940>
Serializers do not commit into the database. You must do this by yourself:
emp = serializer.load(new_employee)
session.add(emp)
session.commit()
For anything beyond Generic Types we must extend the ModelSerializer class:
class EmployeeSerializer(ModelSerializer):
password = Field(load_only=True) # passwords should be only deserialized
company = NestedModelField(Company) # dump company as nested object
serializer = EmployeeSerializer(Employee)
serializer.dump(emp)
# >>
{
"id": 1,
"fullname": "Roberto Silva",
"admission": "2019-04-02T00:00:00",
"company": {"id": 3, "name": "Acme Co"},
}
One of the possibilities is to serialize SQLalchemy joined table inheritance and it child tables as well. To do such it's necessary to set a variable with the desired model class name. Take this Employee class with for instance and let us assume it have a joined table inheritance:
class Employee(Base):
...
type = Column(String(50))
__mapper_args__ = {"polymorphic_identity": "employee", "polymorphic_on": type}
class Engineer(Employee):
__tablename__ = "Engineer"
id = Column(Integer, ForeignKey("employee.id"), primary_key=True)
association = relationship(Association)
__mapper_args__ = {
"polymorphic_identity": "engineer",
}
To use a extended ModelSerializer class on the Engineer class, you should create the serializer as it follows:
class EmployeeSerializer(
PolymorphicModelSerializer
): # Since this class will be polymorphic
password = Field(load_only=True)
company = NestedModelField(Company)
class EngineerSerializer(EmployeeSerializer):
__model_class__ = Engineer # This is the table Serialchemy will refer to
association = NestedModelField(Association)
For guidance on setting up a development environment and how to make a contribution to serialchemy, see the contributing guidelines.
A reminder for the maintainers on how to make a new release.
Note that the VERSION should folow the semantic versioning as X.Y.Z Ex.: v1.0.5
- Create a
release-VERSION
branch fromupstream/master
. - Update
CHANGELOG.rst
. - Push a branch with the changes.
- Once all builds pass, push a
VERSION
tag toupstream
. - Merge the PR.