pythonmarshmallow-sqlalchemy

How to serialize a nested object with the many-to-one relationship in one side?


I face some troubles serializing an object with Marshmallow-sqlAlchemy.

I have two objects:

class Product(Model):
    __tablename__: product_table_name

   id = Column(Integer, primary_key=True)
   name = Column(String)

class BasketItem(Model):
    __tablename__: basket_item_table_name
    id = Column(Integer, primary_key=True)
    product_id = Column(
        Integer, ForeignKey(f"{product_table_name}.id"), nullable=False
    )
    product = relationship("Product", foreign_keys="BasketItem.product_id")

And here is the marshmallow configuration:

class ProductBasketItemSchema(ModelSchema):
    class Meta:
        model = Product
        fields = ("id", "name",)

class BasketItemSchema(ModelSchema):
    class Meta:
        model = BasketItem
        include_relationships = True
        fields = ("id", "product",)
    product: Nested(ProductBasketItemSchema, many=False)

But the output of the basket_item_schema.dump(items) only prints the ID of the products, not the content:

[{'id': 1, 'product': 123}]

instead of

[{'id': 1, 'product': {'id': 123, 'name': 'first'}}]

I think the problem is with the Schema declaration because I can access all the fields of the product before dumping it.

Am I missing something?


Solution

  • After debugging the Marshmallow library, I came into a deadend.

    This can't be done because I'm trying to show the parent from the chid object. This can only be done for one-to-many or many-to-many but not for many-to-one relationships.

    To do so, I've used a custom marshmallow mapper with Schema (instead of ModelSchema) as follows:

    class ProdcutField(Field):
        def __init__(self, **kwargs):
            self.schema = ProductBasketItemSchema()
            super().__init__(**kwargs)
    
        def _serialize(self, value, attr, obj, **kwargs):
            return self.schema.dump(value)
    
    
    class BasketItemSchema(Schema):
        id = fields.Integer()
        product = ProductField()
    

    When using ModelSchema from marshmallow_sqlalchemy, the fields are mapped automatically and it checks the type of relationship. And for many-to-one, it only serialize the id of the object. I've been looking if I can override some functions, but it can't be done this way, so I implemented it manually as shown above.

    I hope this will help somebody else.