python.py 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  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 django.conf import settings
  7. from django.core.serializers import base
  8. from django.db import models
  9. from django.utils.encoding import smart_unicode, is_protected_type
  10. class Serializer(base.Serializer):
  11. """
  12. Serializes a QuerySet to basic Python objects.
  13. """
  14. internal_use_only = True
  15. def start_serialization(self):
  16. self._current = None
  17. self.objects = []
  18. def end_serialization(self):
  19. pass
  20. def start_object(self, obj):
  21. self._current = {}
  22. def end_object(self, obj):
  23. self.objects.append({
  24. "model" : smart_unicode(obj._meta),
  25. "pk" : smart_unicode(obj._get_pk_val(), strings_only=True),
  26. "fields" : self._current
  27. })
  28. self._current = None
  29. def handle_field(self, obj, field):
  30. value = field._get_val_from_obj(obj)
  31. # Protected types (i.e., primitives like None, numbers, dates,
  32. # and Decimals) are passed through as is. All other values are
  33. # converted to string first.
  34. if is_protected_type(value):
  35. self._current[field.name] = value
  36. else:
  37. self._current[field.name] = field.value_to_string(obj)
  38. def handle_fk_field(self, obj, field):
  39. related = getattr(obj, field.name)
  40. if related is not None:
  41. if self.use_natural_keys and hasattr(related, 'natural_key'):
  42. related = related.natural_key()
  43. else:
  44. if field.rel.field_name == related._meta.pk.name:
  45. # Related to remote object via primary key
  46. related = related._get_pk_val()
  47. else:
  48. # Related to remote object via other field
  49. related = smart_unicode(getattr(related, field.rel.field_name), strings_only=True)
  50. self._current[field.name] = related
  51. def handle_m2m_field(self, obj, field):
  52. if field.rel.through._meta.auto_created:
  53. if self.use_natural_keys and hasattr(field.rel.to, 'natural_key'):
  54. m2m_value = lambda value: value.natural_key()
  55. else:
  56. m2m_value = lambda value: smart_unicode(value._get_pk_val(), strings_only=True)
  57. self._current[field.name] = [m2m_value(related)
  58. for related in getattr(obj, field.name).iterator()]
  59. def getvalue(self):
  60. return self.objects
  61. def Deserializer(object_list, **options):
  62. """
  63. Deserialize simple Python objects back into Django ORM instances.
  64. It's expected that you pass the Python objects themselves (instead of a
  65. stream or a string) to the constructor
  66. """
  67. models.get_apps()
  68. for d in object_list:
  69. # Look up the model and starting build a dict of data for it.
  70. Model = _get_model(d["model"])
  71. data = {Model._meta.pk.attname : Model._meta.pk.to_python(d["pk"])}
  72. m2m_data = {}
  73. # Handle each field
  74. for (field_name, field_value) in d["fields"].iteritems():
  75. if isinstance(field_value, str):
  76. field_value = smart_unicode(field_value, options.get("encoding", settings.DEFAULT_CHARSET), strings_only=True)
  77. field = Model._meta.get_field(field_name)
  78. # Handle M2M relations
  79. if field.rel and isinstance(field.rel, models.ManyToManyRel):
  80. if hasattr(field.rel.to._default_manager, 'get_by_natural_key'):
  81. def m2m_convert(value):
  82. if hasattr(value, '__iter__'):
  83. return field.rel.to._default_manager.get_by_natural_key(*value).pk
  84. else:
  85. return smart_unicode(field.rel.to._meta.pk.to_python(value))
  86. else:
  87. m2m_convert = lambda v: smart_unicode(field.rel.to._meta.pk.to_python(v))
  88. m2m_data[field.name] = [m2m_convert(pk) for pk in field_value]
  89. # Handle FK fields
  90. elif field.rel and isinstance(field.rel, models.ManyToOneRel):
  91. if field_value is not None:
  92. if hasattr(field.rel.to._default_manager, 'get_by_natural_key'):
  93. if hasattr(field_value, '__iter__'):
  94. obj = field.rel.to._default_manager.get_by_natural_key(*field_value)
  95. value = getattr(obj, field.rel.field_name)
  96. else:
  97. value = field.rel.to._meta.get_field(field.rel.field_name).to_python(field_value)
  98. data[field.attname] = value
  99. else:
  100. data[field.attname] = field.rel.to._meta.get_field(field.rel.field_name).to_python(field_value)
  101. else:
  102. data[field.attname] = None
  103. # Handle all other fields
  104. else:
  105. data[field.name] = field.to_python(field_value)
  106. yield base.DeserializedObject(Model(**data), m2m_data)
  107. def _get_model(model_identifier):
  108. """
  109. Helper to look up a model from an "app_label.module_name" string.
  110. """
  111. try:
  112. Model = models.get_model(*model_identifier.split("."))
  113. except TypeError:
  114. Model = None
  115. if Model is None:
  116. raise base.DeserializationError(u"Invalid model identifier: '%s'" % model_identifier)
  117. return Model