Search code examples
python-3.xflaskflask-restfulmarshmallow

How to define the same field for load_only and dump_only params at the Marshmallow scheme?


I am trying to build a marshmallow scheme to both load and dump data. And I get everything OK except one field.

Problem description

(If you understand the problem, you don't have to read this).

For load data its type is Decimal. And I used it like this before. Now I want to use this schema for dumping and for that my flask API responses with: TypeError: Object of type Decimal is not JSON serializable. OK, I understand. I changed the type to Float. Then my legacy code started to get an exception while trying to save that field to database (it takes Decimal only). I don't want to change the legacy code so I looked for any solution at the marshmallow docs and found load_only and dump_only params. It seems like those are what I wanted, but here is my problem - I want to set them to the same field. So I just wondered if I can define both fields and tried this:

class PaymentSchema(Schema):
    money = fields.Decimal(load_only=True)
    money = fields.Float(dump_only=True)

I have been expected for a miracle, of course. Actually I was thinking that it will skip first definition (correctly, re-define it). What I got is an absence of the field at all.

Workaround solution

So I tried another solution. I created another schema for dump and inherit it from the former schema:

class PaymentSchema(Schema):
    money = fields.Decimal(load_only=True)

class PaymentDumpSchema(PaymentSchema):
    money = fields.Float(dump_only=True)

It works. But I wonder if there's some another, native, "marshmallow-way" solution for this. I have been looking through the docs but I can't find anything.


Solution

  • pass data_key argument to the field definition

    Documentation mentions, data_key parameter can be used along with dump_only or load_only to be able to have same field with different functionality. attribute parameter's value will refer the field with its original field name.

    So you can write your schema as...

    class PaymentSchema(Schema):
        decimal_money = fields.Decimal(data_key="money", load_only=True, attribute="money")
        money = fields.Float(dump_only=True)
    

    This should solve your problem. I am using data_key for similar problem in marshmallow with SQLAlchemyAutoSchema and this fixed my issue.

    Note: The key in ValidationError.messages (error messages) will be decimal_money by default. You may tweak the handle_error method of Schema class to replace decimal_money with money but it is not recommended as you yourself may not be able to differentiate between the error messages fields.

    Thanks.