summaryrefslogtreecommitdiff
path: root/django/core/serializers/python.py
diff options
context:
space:
mode:
authorAmir Karimi <amk9978@gmail.com>2024-09-12 10:56:18 +0200
committerSarah Boyce <42296566+sarahboyce@users.noreply.github.com>2024-09-17 11:00:49 +0200
commitee5147cfd7de2add74a285537a8968ec074e70cd (patch)
tree64129a4de2722fb78dc486c83656f9d907aab296 /django/core/serializers/python.py
parenta060a22ee2dde7aa29a5a29120087c4864887325 (diff)
Fixed #29522 -- Refactored the Deserializer functions to classes.
Co-authored-by: Emad Mokhtar <emad.mokhtar@veneficus.nl>
Diffstat (limited to 'django/core/serializers/python.py')
-rw-r--r--django/core/serializers/python.py119
1 files changed, 71 insertions, 48 deletions
diff --git a/django/core/serializers/python.py b/django/core/serializers/python.py
index 7ec894aa00..46ef9f0771 100644
--- a/django/core/serializers/python.py
+++ b/django/core/serializers/python.py
@@ -96,45 +96,60 @@ class Serializer(base.Serializer):
return self.objects
-def Deserializer(
- object_list, *, using=DEFAULT_DB_ALIAS, ignorenonexistent=False, **options
-):
+class Deserializer(base.Deserializer):
"""
Deserialize simple Python objects back into Django ORM instances.
It's expected that you pass the Python objects themselves (instead of a
stream or a string) to the constructor
"""
- handle_forward_references = options.pop("handle_forward_references", False)
- field_names_cache = {} # Model: <list of field_names>
- for d in object_list:
+ def __init__(
+ self, object_list, *, using=DEFAULT_DB_ALIAS, ignorenonexistent=False, **options
+ ):
+ super().__init__(object_list, **options)
+ self.handle_forward_references = options.pop("handle_forward_references", False)
+ self.using = using
+ self.ignorenonexistent = ignorenonexistent
+ self.field_names_cache = {} # Model: <list of field_names>
+ self._iterator = None
+
+ def __iter__(self):
+ for obj in self.stream:
+ yield from self._handle_object(obj)
+
+ def __next__(self):
+ if self._iterator is None:
+ self._iterator = iter(self)
+ return next(self._iterator)
+
+ def _handle_object(self, obj):
+ data = {}
+ m2m_data = {}
+ deferred_fields = {}
+
# Look up the model and starting build a dict of data for it.
try:
- Model = _get_model(d["model"])
+ Model = self._get_model_from_node(obj["model"])
except base.DeserializationError:
- if ignorenonexistent:
- continue
- else:
- raise
- data = {}
- if "pk" in d:
+ if self.ignorenonexistent:
+ return
+ raise
+ if "pk" in obj:
try:
- data[Model._meta.pk.attname] = Model._meta.pk.to_python(d.get("pk"))
+ data[Model._meta.pk.attname] = Model._meta.pk.to_python(obj.get("pk"))
except Exception as e:
raise base.DeserializationError.WithData(
- e, d["model"], d.get("pk"), None
+ e, obj["model"], obj.get("pk"), None
)
- m2m_data = {}
- deferred_fields = {}
- if Model not in field_names_cache:
- field_names_cache[Model] = {f.name for f in Model._meta.get_fields()}
- field_names = field_names_cache[Model]
+ if Model not in self.field_names_cache:
+ self.field_names_cache[Model] = {f.name for f in Model._meta.get_fields()}
+ field_names = self.field_names_cache[Model]
# Handle each field
- for field_name, field_value in d["fields"].items():
- if ignorenonexistent and field_name not in field_names:
+ for field_name, field_value in obj["fields"].items():
+ if self.ignorenonexistent and field_name not in field_names:
# skip fields no longer on model
continue
@@ -145,51 +160,59 @@ def Deserializer(
field.remote_field, models.ManyToManyRel
):
try:
- values = base.deserialize_m2m_values(
- field, field_value, using, handle_forward_references
- )
+ values = self._handle_m2m_field_node(field, field_value)
+ if values == base.DEFER_FIELD:
+ deferred_fields[field] = field_value
+ else:
+ m2m_data[field.name] = values
except base.M2MDeserializationError as e:
raise base.DeserializationError.WithData(
- e.original_exc, d["model"], d.get("pk"), e.pk
+ e.original_exc, obj["model"], obj.get("pk"), e.pk
)
- if values == base.DEFER_FIELD:
- deferred_fields[field] = field_value
- else:
- m2m_data[field.name] = values
+
# Handle FK fields
elif field.remote_field and isinstance(
field.remote_field, models.ManyToOneRel
):
try:
- value = base.deserialize_fk_value(
- field, field_value, using, handle_forward_references
- )
+ value = self._handle_fk_field_node(field, field_value)
+ if value == base.DEFER_FIELD:
+ deferred_fields[field] = field_value
+ else:
+ data[field.attname] = value
except Exception as e:
raise base.DeserializationError.WithData(
- e, d["model"], d.get("pk"), field_value
+ e, obj["model"], obj.get("pk"), field_value
)
- if value == base.DEFER_FIELD:
- deferred_fields[field] = field_value
- else:
- data[field.attname] = value
+
# Handle all other fields
else:
try:
data[field.name] = field.to_python(field_value)
except Exception as e:
raise base.DeserializationError.WithData(
- e, d["model"], d.get("pk"), field_value
+ e, obj["model"], obj.get("pk"), field_value
)
- obj = base.build_instance(Model, data, using)
- yield base.DeserializedObject(obj, m2m_data, deferred_fields)
+ model_instance = base.build_instance(Model, data, self.using)
+ yield base.DeserializedObject(model_instance, m2m_data, deferred_fields)
+ def _handle_m2m_field_node(self, field, field_value):
+ return base.deserialize_m2m_values(
+ field, field_value, self.using, self.handle_forward_references
+ )
-def _get_model(model_identifier):
- """Look up a model from an "app_label.model_name" string."""
- try:
- return apps.get_model(model_identifier)
- except (LookupError, TypeError):
- raise base.DeserializationError(
- "Invalid model identifier: '%s'" % model_identifier
+ def _handle_fk_field_node(self, field, field_value):
+ return base.deserialize_fk_value(
+ field, field_value, self.using, self.handle_forward_references
)
+
+ @staticmethod
+ def _get_model_from_node(model_identifier):
+ """Look up a model from an "app_label.model_name" string."""
+ try:
+ return apps.get_model(model_identifier)
+ except (LookupError, TypeError):
+ raise base.DeserializationError(
+ f"Invalid model identifier: {model_identifier}"
+ )