oca-technical/odoo-bringout-oca-rest-framework-model_serializer/model_serializer/field_converter.py
2025-08-29 15:43:03 +02:00

131 lines
3.5 KiB
Python

import logging
from marshmallow import fields
from odoo import fields as odoo_fields
from odoo.addons.datamodel.fields import NestedModel
_logger = logging.getLogger(__name__)
__all__ = ["convert_field"]
class Binary(fields.Raw):
def _serialize(self, value, attr, obj, **kwargs):
res = super()._serialize(value, attr, obj, **kwargs)
if isinstance(res, bytes):
res = res.decode("utf-8")
return res
class FieldConverter:
def __init__(self, odoo_field):
self.odoo_field = odoo_field
def _marshmallow_field_class(self):
pass
def _get_kwargs(self):
kwargs = {
"required": self.odoo_field.required,
"allow_none": not self.odoo_field.required,
}
if self.odoo_field.readonly:
kwargs["dump_only"] = True
return kwargs
def convert_to_marshmallow(self):
marshmallow_field_class = self._marshmallow_field_class()
kwargs = self._get_kwargs()
return marshmallow_field_class(**kwargs)
class BooleanConverter(FieldConverter):
def _get_kwargs(self):
kwargs = super()._get_kwargs()
kwargs["falsy"] = fields.Boolean.falsy.union({None})
return kwargs
def _marshmallow_field_class(self):
return fields.Boolean
class IntegerConverter(FieldConverter):
def _marshmallow_field_class(self):
return fields.Integer
class FloatConverter(FieldConverter):
def _marshmallow_field_class(self):
return fields.Float
class StringConverter(FieldConverter):
def _marshmallow_field_class(self):
return fields.String
class DateConverter(FieldConverter):
def _marshmallow_field_class(self):
return fields.Date
class DatetimeConverter(FieldConverter):
def _marshmallow_field_class(self):
return fields.DateTime
class RawConverter(FieldConverter):
def _marshmallow_field_class(self):
return fields.Raw
class BinaryConverter(FieldConverter):
def _marshmallow_field_class(self):
return Binary
class RelationalConverter(FieldConverter):
def _get_kwargs(self):
kwargs = super()._get_kwargs()
kwargs["many"] = isinstance(
self.odoo_field, (odoo_fields.One2many, odoo_fields.Many2many)
)
kwargs["nested"] = "generic.minimal.serializer"
kwargs["metadata"] = {"odoo_model": self.odoo_field.comodel_name}
return kwargs
def _marshmallow_field_class(self):
return NestedModel
FIELDS_CONV = {
odoo_fields.Boolean: BooleanConverter,
odoo_fields.Integer: IntegerConverter,
odoo_fields.Id: IntegerConverter,
odoo_fields.Float: FloatConverter,
odoo_fields.Monetary: FloatConverter, # should we use a Decimal instead?
odoo_fields.Char: StringConverter,
odoo_fields.Text: StringConverter,
odoo_fields.Html: StringConverter,
odoo_fields.Selection: RawConverter,
odoo_fields.Date: DateConverter,
odoo_fields.Datetime: DatetimeConverter,
odoo_fields.Binary: BinaryConverter,
odoo_fields.Image: BinaryConverter,
odoo_fields.One2many: RelationalConverter,
odoo_fields.Many2one: RelationalConverter,
odoo_fields.Many2many: RelationalConverter,
}
def convert_field(odoo_field):
field_cls = type(odoo_field)
if field_cls in FIELDS_CONV:
return FIELDS_CONV[field_cls](odoo_field).convert_to_marshmallow()
else:
_logger.warning(
"Not implemented: Odoo fields of type {} cannot be "
"translated into Marshmallow fields".format(field_cls)
)