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/serializers/python.py | |
| 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/serializers/python.py')
| -rw-r--r-- | django/core/serializers/python.py | 119 |
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}" + ) |
