python.py 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  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 django.apps import apps
  8. from django.conf import settings
  9. from django.core.serializers import base
  10. from django.db import models, DEFAULT_DB_ALIAS
  11. from django.utils.encoding import smart_text, is_protected_type
  12. from django.utils import six
  13. class Serializer(base.Serializer):
  14. """
  15. Serializes a QuerySet to basic Python objects.
  16. """
  17. internal_use_only = True
  18. def start_serialization(self):
  19. self._current = None
  20. self.objects = []
  21. def end_serialization(self):
  22. pass
  23. def start_object(self, obj):
  24. self._current = {}
  25. def end_object(self, obj):
  26. self.objects.append(self.get_dump_object(obj))
  27. self._current = None
  28. def get_dump_object(self, obj):
  29. data = {
  30. "model": smart_text(obj._meta),
  31. "fields": self._current,
  32. }
  33. if not self.use_natural_primary_keys or not hasattr(obj, 'natural_key'):
  34. data["pk"] = smart_text(obj._get_pk_val(), strings_only=True)
  35. return data
  36. def handle_field(self, obj, field):
  37. value = field._get_val_from_obj(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.rel.to, '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. self._current[field.name] = value
  55. def handle_m2m_field(self, obj, field):
  56. if field.rel.through._meta.auto_created:
  57. if self.use_natural_foreign_keys and hasattr(field.rel.to, 'natural_key'):
  58. m2m_value = lambda value: value.natural_key()
  59. else:
  60. m2m_value = lambda value: smart_text(value._get_pk_val(), strings_only=True)
  61. self._current[field.name] = [m2m_value(related)
  62. for related in getattr(obj, field.name).iterator()]
  63. def getvalue(self):
  64. return self.objects
  65. def Deserializer(object_list, **options):
  66. """
  67. Deserialize simple Python objects back into Django ORM instances.
  68. It's expected that you pass the Python objects themselves (instead of a
  69. stream or a string) to the constructor
  70. """
  71. db = options.pop('using', DEFAULT_DB_ALIAS)
  72. ignore = options.pop('ignorenonexistent', False)
  73. for d in object_list:
  74. # Look up the model and starting build a dict of data for it.
  75. Model = _get_model(d["model"])
  76. data = {}
  77. if 'pk' in d:
  78. data[Model._meta.pk.attname] = Model._meta.pk.to_python(d.get("pk", None))
  79. m2m_data = {}
  80. model_fields = Model._meta.get_all_field_names()
  81. # Handle each field
  82. for (field_name, field_value) in six.iteritems(d["fields"]):
  83. if ignore and field_name not in model_fields:
  84. # skip fields no longer on model
  85. continue
  86. if isinstance(field_value, str):
  87. field_value = smart_text(field_value, options.get("encoding", settings.DEFAULT_CHARSET), strings_only=True)
  88. field = Model._meta.get_field(field_name)
  89. # Handle M2M relations
  90. if field.rel and isinstance(field.rel, models.ManyToManyRel):
  91. if hasattr(field.rel.to._default_manager, 'get_by_natural_key'):
  92. def m2m_convert(value):
  93. if hasattr(value, '__iter__') and not isinstance(value, six.text_type):
  94. return field.rel.to._default_manager.db_manager(db).get_by_natural_key(*value).pk
  95. else:
  96. return smart_text(field.rel.to._meta.pk.to_python(value))
  97. else:
  98. m2m_convert = lambda v: smart_text(field.rel.to._meta.pk.to_python(v))
  99. m2m_data[field.name] = [m2m_convert(pk) for pk in field_value]
  100. # Handle FK fields
  101. elif field.rel and isinstance(field.rel, models.ManyToOneRel):
  102. if field_value is not None:
  103. if hasattr(field.rel.to._default_manager, 'get_by_natural_key'):
  104. if hasattr(field_value, '__iter__') and not isinstance(field_value, six.text_type):
  105. obj = field.rel.to._default_manager.db_manager(db).get_by_natural_key(*field_value)
  106. value = getattr(obj, field.rel.field_name)
  107. # If this is a natural foreign key to an object that
  108. # has a FK/O2O as the foreign key, use the FK value
  109. if field.rel.to._meta.pk.rel:
  110. value = value.pk
  111. else:
  112. value = field.rel.to._meta.get_field(field.rel.field_name).to_python(field_value)
  113. data[field.attname] = value
  114. else:
  115. data[field.attname] = field.rel.to._meta.get_field(field.rel.field_name).to_python(field_value)
  116. else:
  117. data[field.attname] = None
  118. # Handle all other fields
  119. else:
  120. data[field.name] = field.to_python(field_value)
  121. obj = base.build_instance(Model, data, db)
  122. yield base.DeserializedObject(obj, m2m_data)
  123. def _get_model(model_identifier):
  124. """
  125. Helper to look up a model from an "app_label.model_name" string.
  126. """
  127. try:
  128. return apps.get_model(model_identifier)
  129. except (LookupError, TypeError):
  130. raise base.DeserializationError("Invalid model identifier: '%s'" % model_identifier)