base.py 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. """
  2. Module for abstract serializer/unserializer base classes.
  3. """
  4. import warnings
  5. from django.db import models
  6. from django.utils import six
  7. class SerializerDoesNotExist(KeyError):
  8. """The requested serializer was not found."""
  9. pass
  10. class SerializationError(Exception):
  11. """Something bad happened during serialization."""
  12. pass
  13. class DeserializationError(Exception):
  14. """Something bad happened during deserialization."""
  15. pass
  16. class Serializer(object):
  17. """
  18. Abstract serializer base class.
  19. """
  20. # Indicates if the implemented serializer is only available for
  21. # internal Django use.
  22. internal_use_only = False
  23. def serialize(self, queryset, **options):
  24. """
  25. Serialize a queryset.
  26. """
  27. self.options = options
  28. self.stream = options.pop("stream", six.StringIO())
  29. self.selected_fields = options.pop("fields", None)
  30. self.use_natural_keys = options.pop("use_natural_keys", False)
  31. if self.use_natural_keys:
  32. warnings.warn("``use_natural_keys`` is deprecated; use ``use_natural_foreign_keys`` instead.",
  33. PendingDeprecationWarning)
  34. self.use_natural_foreign_keys = options.pop('use_natural_foreign_keys', False) or self.use_natural_keys
  35. self.use_natural_primary_keys = options.pop('use_natural_primary_keys', False)
  36. self.start_serialization()
  37. self.first = True
  38. for obj in queryset:
  39. self.start_object(obj)
  40. # Use the concrete parent class' _meta instead of the object's _meta
  41. # This is to avoid local_fields problems for proxy models. Refs #17717.
  42. concrete_model = obj._meta.concrete_model
  43. for field in concrete_model._meta.local_fields:
  44. if field.serialize:
  45. if field.rel is None:
  46. if self.selected_fields is None or field.attname in self.selected_fields:
  47. self.handle_field(obj, field)
  48. else:
  49. if self.selected_fields is None or field.attname[:-3] in self.selected_fields:
  50. self.handle_fk_field(obj, field)
  51. for field in concrete_model._meta.many_to_many:
  52. if field.serialize:
  53. if self.selected_fields is None or field.attname in self.selected_fields:
  54. self.handle_m2m_field(obj, field)
  55. self.end_object(obj)
  56. if self.first:
  57. self.first = False
  58. self.end_serialization()
  59. return self.getvalue()
  60. def start_serialization(self):
  61. """
  62. Called when serializing of the queryset starts.
  63. """
  64. raise NotImplementedError('subclasses of Serializer must provide a start_serialization() method')
  65. def end_serialization(self):
  66. """
  67. Called when serializing of the queryset ends.
  68. """
  69. pass
  70. def start_object(self, obj):
  71. """
  72. Called when serializing of an object starts.
  73. """
  74. raise NotImplementedError('subclasses of Serializer must provide a start_object() method')
  75. def end_object(self, obj):
  76. """
  77. Called when serializing of an object ends.
  78. """
  79. pass
  80. def handle_field(self, obj, field):
  81. """
  82. Called to handle each individual (non-relational) field on an object.
  83. """
  84. raise NotImplementedError('subclasses of Serializer must provide an handle_field() method')
  85. def handle_fk_field(self, obj, field):
  86. """
  87. Called to handle a ForeignKey field.
  88. """
  89. raise NotImplementedError('subclasses of Serializer must provide an handle_fk_field() method')
  90. def handle_m2m_field(self, obj, field):
  91. """
  92. Called to handle a ManyToManyField.
  93. """
  94. raise NotImplementedError('subclasses of Serializer must provide an handle_m2m_field() method')
  95. def getvalue(self):
  96. """
  97. Return the fully serialized queryset (or None if the output stream is
  98. not seekable).
  99. """
  100. if callable(getattr(self.stream, 'getvalue', None)):
  101. return self.stream.getvalue()
  102. class Deserializer(six.Iterator):
  103. """
  104. Abstract base deserializer class.
  105. """
  106. def __init__(self, stream_or_string, **options):
  107. """
  108. Init this serializer given a stream or a string
  109. """
  110. self.options = options
  111. if isinstance(stream_or_string, six.string_types):
  112. self.stream = six.StringIO(stream_or_string)
  113. else:
  114. self.stream = stream_or_string
  115. def __iter__(self):
  116. return self
  117. def __next__(self):
  118. """Iteration iterface -- return the next item in the stream"""
  119. raise NotImplementedError('subclasses of Deserializer must provide a __next__() method')
  120. class DeserializedObject(object):
  121. """
  122. A deserialized model.
  123. Basically a container for holding the pre-saved deserialized data along
  124. with the many-to-many data saved with the object.
  125. Call ``save()`` to save the object (with the many-to-many data) to the
  126. database; call ``save(save_m2m=False)`` to save just the object fields
  127. (and not touch the many-to-many stuff.)
  128. """
  129. def __init__(self, obj, m2m_data=None):
  130. self.object = obj
  131. self.m2m_data = m2m_data
  132. def __repr__(self):
  133. return "<DeserializedObject: %s.%s(pk=%s)>" % (
  134. self.object._meta.app_label, self.object._meta.object_name, self.object.pk)
  135. def save(self, save_m2m=True, using=None):
  136. # Call save on the Model baseclass directly. This bypasses any
  137. # model-defined save. The save is also forced to be raw.
  138. # raw=True is passed to any pre/post_save signals.
  139. models.Model.save_base(self.object, using=using, raw=True)
  140. if self.m2m_data and save_m2m:
  141. for accessor_name, object_list in self.m2m_data.items():
  142. setattr(self.object, accessor_name, object_list)
  143. # prevent a second (possibly accidental) call to save() from saving
  144. # the m2m data twice.
  145. self.m2m_data = None
  146. def build_instance(Model, data, db):
  147. """
  148. Build a model instance.
  149. If the model instance doesn't have a primary key and the model supports
  150. natural keys, try to retrieve it from the database.
  151. """
  152. obj = Model(**data)
  153. if (obj.pk is None and hasattr(Model, 'natural_key') and
  154. hasattr(Model._default_manager, 'get_by_natural_key')):
  155. natural_key = obj.natural_key()
  156. try:
  157. obj.pk = Model._default_manager.db_manager(db).get_by_natural_key(*natural_key).pk
  158. except Model.DoesNotExist:
  159. pass
  160. return obj