python.py 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. """
  2. A Python "serializer". Doesn't do much serializing per se -- just converts to
  3. and from basic Python data types (lists, dicts, strings, etc.). Useful as a basis for
  4. other serializers.
  5. """
  6. from __future__ import unicode_literals
  7. from collections import OrderedDict
  8. from django.apps import apps
  9. from django.conf import settings
  10. from django.core.serializers import base
  11. from django.db import DEFAULT_DB_ALIAS, models
  12. from django.utils import six
  13. from django.utils.encoding import force_text, is_protected_type
  14. class Serializer(base.Serializer):
  15. """
  16. Serializes a QuerySet to basic Python objects.
  17. """
  18. internal_use_only = True
  19. def start_serialization(self):
  20. self._current = None
  21. self.objects = []
  22. def end_serialization(self):
  23. pass
  24. def start_object(self, obj):
  25. self._current = OrderedDict()
  26. def end_object(self, obj):
  27. self.objects.append(self.get_dump_object(obj))
  28. self._current = None
  29. def get_dump_object(self, obj):
  30. model = obj._meta.proxy_for_model if obj._deferred else obj.__class__
  31. data = OrderedDict([('model', force_text(model._meta))])
  32. if not self.use_natural_primary_keys or not hasattr(obj, 'natural_key'):
  33. data["pk"] = force_text(obj._get_pk_val(), strings_only=True)
  34. data['fields'] = self._current
  35. return data
  36. def handle_field(self, obj, field):
  37. value = field.value_from_object(obj)
  38. # Protected types (i.e., primitives like None, numbers, dates,
  39. # and Decimals) are passed through as is. All other values are
  40. # converted to string first.
  41. if is_protected_type(value):
  42. self._current[field.name] = value
  43. else:
  44. self._current[field.name] = field.value_to_string(obj)
  45. def handle_fk_field(self, obj, field):
  46. if self.use_natural_foreign_keys and hasattr(field.remote_field.model, 'natural_key'):
  47. related = getattr(obj, field.name)
  48. if related:
  49. value = related.natural_key()
  50. else:
  51. value = None
  52. else:
  53. value = getattr(obj, field.get_attname())
  54. if not is_protected_type(value):
  55. value = field.value_to_string(obj)
  56. self._current[field.name] = value
  57. def handle_m2m_field(self, obj, field):
  58. if field.remote_field.through._meta.auto_created:
  59. if self.use_natural_foreign_keys and hasattr(field.remote_field.model, 'natural_key'):
  60. def m2m_value(value):
  61. return value.natural_key()
  62. else:
  63. def m2m_value(value):
  64. return force_text(value._get_pk_val(), strings_only=True)
  65. self._current[field.name] = [
  66. m2m_value(related) for related in getattr(obj, field.name).iterator()
  67. ]
  68. def getvalue(self):
  69. return self.objects
  70. def Deserializer(object_list, **options):
  71. """
  72. Deserialize simple Python objects back into Django ORM instances.
  73. It's expected that you pass the Python objects themselves (instead of a
  74. stream or a string) to the constructor
  75. """
  76. db = options.pop('using', DEFAULT_DB_ALIAS)
  77. ignore = options.pop('ignorenonexistent', False)
  78. field_names_cache = {} # Model: <list of field_names>
  79. for d in object_list:
  80. # Look up the model and starting build a dict of data for it.
  81. try:
  82. Model = _get_model(d["model"])
  83. except base.DeserializationError:
  84. if ignore:
  85. continue
  86. else:
  87. raise
  88. data = {}
  89. if 'pk' in d:
  90. try:
  91. data[Model._meta.pk.attname] = Model._meta.pk.to_python(d.get('pk'))
  92. except Exception as e:
  93. raise base.DeserializationError.WithData(e, d['model'], d.get('pk'), None)
  94. m2m_data = {}
  95. if Model not in field_names_cache:
  96. field_names_cache[Model] = {f.name for f in Model._meta.get_fields()}
  97. field_names = field_names_cache[Model]
  98. # Handle each field
  99. for (field_name, field_value) in six.iteritems(d["fields"]):
  100. if ignore and field_name not in field_names:
  101. # skip fields no longer on model
  102. continue
  103. if isinstance(field_value, str):
  104. field_value = force_text(
  105. field_value, options.get("encoding", settings.DEFAULT_CHARSET), strings_only=True
  106. )
  107. field = Model._meta.get_field(field_name)
  108. # Handle M2M relations
  109. if field.remote_field and isinstance(field.remote_field, models.ManyToManyRel):
  110. model = field.remote_field.model
  111. if hasattr(model._default_manager, 'get_by_natural_key'):
  112. def m2m_convert(value):
  113. if hasattr(value, '__iter__') and not isinstance(value, six.text_type):
  114. return model._default_manager.db_manager(db).get_by_natural_key(*value).pk
  115. else:
  116. return force_text(model._meta.pk.to_python(value), strings_only=True)
  117. else:
  118. def m2m_convert(v):
  119. return force_text(model._meta.pk.to_python(v), strings_only=True)
  120. try:
  121. m2m_data[field.name] = []
  122. for pk in field_value:
  123. m2m_data[field.name].append(m2m_convert(pk))
  124. except Exception as e:
  125. raise base.DeserializationError.WithData(e, d['model'], d.get('pk'), pk)
  126. # Handle FK fields
  127. elif field.remote_field and isinstance(field.remote_field, models.ManyToOneRel):
  128. model = field.remote_field.model
  129. if field_value is not None:
  130. try:
  131. default_manager = model._default_manager
  132. field_name = field.remote_field.field_name
  133. if hasattr(default_manager, 'get_by_natural_key'):
  134. if hasattr(field_value, '__iter__') and not isinstance(field_value, six.text_type):
  135. obj = default_manager.db_manager(db).get_by_natural_key(*field_value)
  136. value = getattr(obj, field.remote_field.field_name)
  137. # If this is a natural foreign key to an object that
  138. # has a FK/O2O as the foreign key, use the FK value
  139. if model._meta.pk.remote_field:
  140. value = value.pk
  141. else:
  142. value = model._meta.get_field(field_name).to_python(field_value)
  143. data[field.attname] = value
  144. else:
  145. data[field.attname] = model._meta.get_field(field_name).to_python(field_value)
  146. except Exception as e:
  147. raise base.DeserializationError.WithData(e, d['model'], d.get('pk'), field_value)
  148. else:
  149. data[field.attname] = None
  150. # Handle all other fields
  151. else:
  152. try:
  153. data[field.name] = field.to_python(field_value)
  154. except Exception as e:
  155. raise base.DeserializationError.WithData(e, d['model'], d.get('pk'), field_value)
  156. obj = base.build_instance(Model, data, db)
  157. yield base.DeserializedObject(obj, m2m_data)
  158. def _get_model(model_identifier):
  159. """
  160. Helper to look up a model from an "app_label.model_name" string.
  161. """
  162. try:
  163. return apps.get_model(model_identifier)
  164. except (LookupError, TypeError):
  165. raise base.DeserializationError("Invalid model identifier: '%s'" % model_identifier)