#36986 new Cleanup/optimization

Add a hook to Serializer._value_from_field() for complex fields like CompositePrimaryKey

Reported by: Tim Graham Owned by:
Component: Core (Serialization) Version: dev
Severity: Normal Keywords:
Cc: Triage Stage: Unreviewed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

The Python serializer's _value_from_field() method using isinstance for special-case handling of CompositePrimaryKey.

Custom model fields can't add similar logic without subclassing the serializer, a cumbersome solution which doesn't scale well in the presence of multiple custom fields.

My motivation for a generic hook is to add serialization support for MongoDB's EmbeddedModelField, EmbeddedModelArrayField, PolymorphicEmbeddedModelField, and PolymorphicEmbeddedModelArrayField.

An early prototype:

  • django/core/serializers/python.py

    diff --git a/django/core/serializers/python.py b/django/core/serializers/python.py
    index 2929874b01..1a4f5a1336 100644
    a b class Serializer(base.Serializer):  
    3939        data["fields"] = self._current
    4040        return data
    4141
     42    def get_fields_from_model(self, obj, *, polymorphic=False):
     43        data = {
     44            field.name: self._value_from_field(obj, field)
     45            for field in obj._meta.local_fields
     46        }
     47        if polymorphic:
     48            data["_label"] = obj._meta.label
     49        return data
     50
    4251    def _value_from_field(self, obj, field):
    4352        if isinstance(field, CompositePrimaryKey):
    4453            return [self._value_from_field(obj, f) for f in field]
     54        if hasattr(field, "embedded_model"):
     55            sub_obj = getattr(obj, field.attname)
     56            if sub_obj is None:
     57                return None
     58            if isinstance(sub_obj, list):
     59                return [self.get_fields_from_model(sub) for sub in sub_obj]
     60            return self.get_fields_from_model(sub_obj)
     61        if hasattr(field, "embedded_models"):
     62            sub_obj = getattr(obj, field.attname)
     63            if sub_obj is None:
     64                return None
     65            if isinstance(sub_obj, list):
     66                return [
     67                    self.get_fields_from_model(sub, polymorphic=True) for sub in sub_obj
     68                ]
     69            return self.get_fields_from_model(sub_obj, polymorphic=True)
    4570        value = field.value_from_object(obj)
    4671        # Protected types (i.e., primitives like None, numbers, dates,
    4772        # and Decimals) are passed through as is. All other values are

I don't have an immediate proposal for the API change to accomplish this.

Change History (0)

Note: See TracTickets for help on using tickets.
Back to Top