123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156 |
- """
- A Python "serializer". Doesn't do much serializing per se -- just converts to
- and from basic Python data types (lists, dicts, strings, etc.). Useful as a basis for
- other serializers.
- """
- from collections import OrderedDict
-
- from django.apps import apps
- from django.core.serializers import base
- from django.db import DEFAULT_DB_ALIAS, models
- from django.utils.encoding import is_protected_type
-
-
- class Serializer(base.Serializer):
- """
- Serialize a QuerySet to basic Python objects.
- """
-
- internal_use_only = True
-
- def start_serialization(self):
- self._current = None
- self.objects = []
-
- def end_serialization(self):
- pass
-
- def start_object(self, obj):
- self._current = OrderedDict()
-
- def end_object(self, obj):
- self.objects.append(self.get_dump_object(obj))
- self._current = None
-
- def get_dump_object(self, obj):
- data = OrderedDict([('model', str(obj._meta))])
- if not self.use_natural_primary_keys or not hasattr(obj, 'natural_key'):
- data["pk"] = self._value_from_field(obj, obj._meta.pk)
- data['fields'] = self._current
- return data
-
- def _value_from_field(self, obj, field):
- 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
- # converted to string first.
- return value if is_protected_type(value) else field.value_to_string(obj)
-
- def handle_field(self, obj, field):
- self._current[field.name] = self._value_from_field(obj, field)
-
- def handle_fk_field(self, obj, field):
- if self.use_natural_foreign_keys and hasattr(field.remote_field.model, 'natural_key'):
- related = getattr(obj, field.name)
- if related:
- value = related.natural_key()
- else:
- value = None
- else:
- value = self._value_from_field(obj, field)
- self._current[field.name] = value
-
- def handle_m2m_field(self, obj, field):
- if field.remote_field.through._meta.auto_created:
- if self.use_natural_foreign_keys and hasattr(field.remote_field.model, 'natural_key'):
- def m2m_value(value):
- return value.natural_key()
- else:
- def m2m_value(value):
- return self._value_from_field(value, value._meta.pk)
- self._current[field.name] = [
- m2m_value(related) for related in getattr(obj, field.name).iterator()
- ]
-
- def getvalue(self):
- return self.objects
-
-
- def Deserializer(object_list, *, using=DEFAULT_DB_ALIAS, ignorenonexistent=False, **options):
- """
- 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:
- # Look up the model and starting build a dict of data for it.
- try:
- Model = _get_model(d["model"])
- except base.DeserializationError:
- if ignorenonexistent:
- continue
- else:
- raise
- data = {}
- if 'pk' in d:
- try:
- data[Model._meta.pk.attname] = Model._meta.pk.to_python(d.get('pk'))
- except Exception as e:
- raise base.DeserializationError.WithData(e, d['model'], d.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]
-
- # Handle each field
- for (field_name, field_value) in d["fields"].items():
-
- if ignorenonexistent and field_name not in field_names:
- # skip fields no longer on model
- continue
-
- field = Model._meta.get_field(field_name)
-
- # Handle M2M relations
- if field.remote_field and isinstance(field.remote_field, models.ManyToManyRel):
- try:
- values = base.deserialize_m2m_values(field, field_value, using, handle_forward_references)
- except base.M2MDeserializationError as e:
- raise base.DeserializationError.WithData(e.original_exc, d['model'], d.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)
- except Exception as e:
- raise base.DeserializationError.WithData(e, d['model'], d.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)
-
- obj = base.build_instance(Model, data, using)
- yield base.DeserializedObject(obj, m2m_data, deferred_fields)
-
-
- 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)
|