#36986: Add a hook to Serializer._value_from_field() for complex fields like
CompositePrimaryKey
-------------------------------------+-------------------------------------
Reporter: Tim Graham | Type:
| Cleanup/optimization
Status: new | Component: Core
| (Serialization)
Version: dev | Severity: Normal
Keywords: | Triage Stage:
| Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
The Python serializer's `_value_from_field()` method using `isinstance`
for
[
https://github.com/django/django/blob/main/django/core/serializers/python.py#L43-L44
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 [
https://django-mongodb-
backend.readthedocs.io/en/latest/ref/models/fields/#embeddedmodelfield
EmbeddedModelField], `EmbeddedModelArrayField`,
`PolymorphicEmbeddedModelField`, and `PolymorphicEmbeddedModelArrayField`.
An early prototype:
{{{#!diff
diff --git a/django/core/serializers/python.py
b/django/core/serializers/python.py
index 2929874b01..1a4f5a1336 100644
--- a/django/core/serializers/python.py
+++ b/django/core/serializers/python.py
@@ -39,9 +39,34 @@ class Serializer(base.Serializer):
data["fields"] = self._current
return data
+ def get_fields_from_model(self, obj, *, polymorphic=False):
+ data = {
+
field.name: self._value_from_field(obj, field)
+ for field in obj._meta.local_fields
+ }
+ if polymorphic:
+ data["_label"] = obj._meta.label
+ return data
+
def _value_from_field(self, obj, field):
if isinstance(field, CompositePrimaryKey):
return [self._value_from_field(obj, f) for f in field]
+ if hasattr(field, "embedded_model"):
+ sub_obj = getattr(obj, field.attname)
+ if sub_obj is None:
+ return None
+ if isinstance(sub_obj, list):
+ return [self.get_fields_from_model(sub) for sub in
sub_obj]
+ return self.get_fields_from_model(sub_obj)
+ if hasattr(field, "embedded_models"):
+ sub_obj = getattr(obj, field.attname)
+ if sub_obj is None:
+ return None
+ if isinstance(sub_obj, list):
+ return [
+ self.get_fields_from_model(sub, polymorphic=True) for
sub in sub_obj
+ ]
+ return self.get_fields_from_model(sub_obj, polymorphic=True)
value = field.value_from_object(obj)
# Protected types (i.e., primitives like None, numbers, dates,
# 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.
--
Ticket URL: <
https://code.djangoproject.com/ticket/36986>
Django <
https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.