base.py 6.7 KB

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