summaryrefslogtreecommitdiff
path: root/django/core
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
parenta060a22ee2dde7aa29a5a29120087c4864887325 (diff)
Fixed #29522 -- Refactored the Deserializer functions to classes.
Co-authored-by: Emad Mokhtar <emad.mokhtar@veneficus.nl>
Diffstat (limited to 'django/core')
-rw-r--r--django/core/serializers/json.py32
-rw-r--r--django/core/serializers/jsonl.py31
-rw-r--r--django/core/serializers/python.py119
-rw-r--r--django/core/serializers/pyyaml.py31
4 files changed, 130 insertions, 83 deletions
diff --git a/django/core/serializers/json.py b/django/core/serializers/json.py
index afac821465..7683368e62 100644
--- a/django/core/serializers/json.py
+++ b/django/core/serializers/json.py
@@ -59,19 +59,27 @@ class Serializer(PythonSerializer):
return super(PythonSerializer, self).getvalue()
-def Deserializer(stream_or_string, **options):
+class Deserializer(PythonDeserializer):
"""Deserialize a stream or string of JSON data."""
- if not isinstance(stream_or_string, (bytes, str)):
- stream_or_string = stream_or_string.read()
- if isinstance(stream_or_string, bytes):
- stream_or_string = stream_or_string.decode()
- try:
- objects = json.loads(stream_or_string)
- yield from PythonDeserializer(objects, **options)
- except (GeneratorExit, DeserializationError):
- raise
- except Exception as exc:
- raise DeserializationError() from exc
+
+ def __init__(self, stream_or_string, **options):
+ if not isinstance(stream_or_string, (bytes, str)):
+ stream_or_string = stream_or_string.read()
+ if isinstance(stream_or_string, bytes):
+ stream_or_string = stream_or_string.decode()
+ try:
+ objects = json.loads(stream_or_string)
+ except Exception as exc:
+ raise DeserializationError() from exc
+ super().__init__(objects, **options)
+
+ def _handle_object(self, obj):
+ try:
+ yield from super()._handle_object(obj)
+ except (GeneratorExit, DeserializationError):
+ raise
+ except Exception as exc:
+ raise DeserializationError(f"Error deserializing object: {exc}") from exc
class DjangoJSONEncoder(json.JSONEncoder):
diff --git a/django/core/serializers/jsonl.py b/django/core/serializers/jsonl.py
index c264c2ccaf..7bc9bed79f 100644
--- a/django/core/serializers/jsonl.py
+++ b/django/core/serializers/jsonl.py
@@ -39,19 +39,30 @@ class Serializer(PythonSerializer):
return super(PythonSerializer, self).getvalue()
-def Deserializer(stream_or_string, **options):
+class Deserializer(PythonDeserializer):
"""Deserialize a stream or string of JSON data."""
- if isinstance(stream_or_string, bytes):
- stream_or_string = stream_or_string.decode()
- if isinstance(stream_or_string, (bytes, str)):
- stream_or_string = stream_or_string.split("\n")
- for line in stream_or_string:
- if not line.strip():
- continue
+ def __init__(self, stream_or_string, **options):
+ if isinstance(stream_or_string, bytes):
+ stream_or_string = stream_or_string.decode()
+ if isinstance(stream_or_string, str):
+ stream_or_string = stream_or_string.splitlines()
+ super().__init__(Deserializer._get_lines(stream_or_string), **options)
+
+ def _handle_object(self, obj):
try:
- yield from PythonDeserializer([json.loads(line)], **options)
+ yield from super()._handle_object(obj)
except (GeneratorExit, DeserializationError):
raise
except Exception as exc:
- raise DeserializationError() from exc
+ raise DeserializationError(f"Error deserializing object: {exc}") from exc
+
+ @staticmethod
+ def _get_lines(stream):
+ for line in stream:
+ if not line.strip():
+ continue
+ try:
+ yield json.loads(line)
+ except Exception as exc:
+ raise DeserializationError() from exc
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}"
+ )
diff --git a/django/core/serializers/pyyaml.py b/django/core/serializers/pyyaml.py
index 9a20b6658f..ed6e4b3895 100644
--- a/django/core/serializers/pyyaml.py
+++ b/django/core/serializers/pyyaml.py
@@ -6,7 +6,6 @@ Requires PyYaml (https://pyyaml.org/), but that's checked for in __init__.
import collections
import decimal
-from io import StringIO
import yaml
@@ -66,17 +65,23 @@ class Serializer(PythonSerializer):
return super(PythonSerializer, self).getvalue()
-def Deserializer(stream_or_string, **options):
+class Deserializer(PythonDeserializer):
"""Deserialize a stream or string of YAML data."""
- if isinstance(stream_or_string, bytes):
- stream_or_string = stream_or_string.decode()
- if isinstance(stream_or_string, str):
- stream = StringIO(stream_or_string)
- else:
+
+ def __init__(self, stream_or_string, **options):
stream = stream_or_string
- try:
- yield from PythonDeserializer(yaml.load(stream, Loader=SafeLoader), **options)
- except (GeneratorExit, DeserializationError):
- raise
- except Exception as exc:
- raise DeserializationError() from exc
+ if isinstance(stream_or_string, bytes):
+ stream = stream_or_string.decode()
+ try:
+ objects = yaml.load(stream, Loader=SafeLoader)
+ except Exception as exc:
+ raise DeserializationError() from exc
+ super().__init__(objects, **options)
+
+ def _handle_object(self, obj):
+ try:
+ yield from super()._handle_object(obj)
+ except (GeneratorExit, DeserializationError):
+ raise
+ except Exception as exc:
+ raise DeserializationError(f"Error deserializing object: {exc}") from exc