diff options
| author | Amir Karimi <amk9978@gmail.com> | 2024-09-12 10:56:18 +0200 |
|---|---|---|
| committer | Sarah Boyce <42296566+sarahboyce@users.noreply.github.com> | 2024-09-17 11:00:49 +0200 |
| commit | ee5147cfd7de2add74a285537a8968ec074e70cd (patch) | |
| tree | 64129a4de2722fb78dc486c83656f9d907aab296 /django/core | |
| parent | a060a22ee2dde7aa29a5a29120087c4864887325 (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.py | 32 | ||||
| -rw-r--r-- | django/core/serializers/jsonl.py | 31 | ||||
| -rw-r--r-- | django/core/serializers/python.py | 119 | ||||
| -rw-r--r-- | django/core/serializers/pyyaml.py | 31 |
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 |
