2006-06-29 00:00:37 +08:00
|
|
|
"""
|
|
|
|
Module for abstract serializer/unserializer base classes.
|
|
|
|
"""
|
|
|
|
from django.db import models
|
2012-07-20 20:22:00 +08:00
|
|
|
from django.utils import six
|
2006-06-29 00:00:37 +08:00
|
|
|
|
2013-11-03 04:12:09 +08:00
|
|
|
|
2011-04-27 00:49:32 +08:00
|
|
|
class SerializerDoesNotExist(KeyError):
|
|
|
|
"""The requested serializer was not found."""
|
|
|
|
pass
|
|
|
|
|
2013-11-03 04:12:09 +08:00
|
|
|
|
2006-06-29 00:00:37 +08:00
|
|
|
class SerializationError(Exception):
|
|
|
|
"""Something bad happened during serialization."""
|
|
|
|
pass
|
2006-08-31 12:11:46 +08:00
|
|
|
|
2013-11-03 04:12:09 +08:00
|
|
|
|
2006-06-29 00:00:37 +08:00
|
|
|
class DeserializationError(Exception):
|
|
|
|
"""Something bad happened during deserialization."""
|
2015-03-27 02:31:09 +08:00
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def WithData(cls, original_exc, model, fk, field_value):
|
|
|
|
"""
|
|
|
|
Factory method for creating a deserialization error which has a more
|
|
|
|
explanatory messsage.
|
|
|
|
"""
|
|
|
|
return cls("%s: (%s:pk=%s) field_value was '%s'" % (original_exc, model, fk, field_value))
|
2006-06-29 00:00:37 +08:00
|
|
|
|
2013-11-03 04:12:09 +08:00
|
|
|
|
2006-06-29 00:00:37 +08:00
|
|
|
class Serializer(object):
|
|
|
|
"""
|
|
|
|
Abstract serializer base class.
|
|
|
|
"""
|
2006-08-31 12:11:46 +08:00
|
|
|
|
2008-03-18 22:35:05 +08:00
|
|
|
# Indicates if the implemented serializer is only available for
|
2007-12-17 14:53:15 +08:00
|
|
|
# internal Django use.
|
|
|
|
internal_use_only = False
|
2008-03-18 22:35:05 +08:00
|
|
|
|
2006-06-29 00:00:37 +08:00
|
|
|
def serialize(self, queryset, **options):
|
|
|
|
"""
|
|
|
|
Serialize a queryset.
|
|
|
|
"""
|
|
|
|
self.options = options
|
2006-08-31 12:11:46 +08:00
|
|
|
|
2012-08-15 01:48:38 +08:00
|
|
|
self.stream = options.pop("stream", six.StringIO())
|
2011-01-08 23:07:45 +08:00
|
|
|
self.selected_fields = options.pop("fields", None)
|
2015-01-18 09:18:34 +08:00
|
|
|
self.use_natural_foreign_keys = options.pop('use_natural_foreign_keys', False)
|
2012-08-01 09:49:01 +08:00
|
|
|
self.use_natural_primary_keys = options.pop('use_natural_primary_keys', False)
|
2006-08-31 12:11:46 +08:00
|
|
|
|
2006-06-29 00:00:37 +08:00
|
|
|
self.start_serialization()
|
2012-05-26 17:43:37 +08:00
|
|
|
self.first = True
|
2006-06-29 00:00:37 +08:00
|
|
|
for obj in queryset:
|
|
|
|
self.start_object(obj)
|
2012-03-04 01:50:18 +08:00
|
|
|
# Use the concrete parent class' _meta instead of the object's _meta
|
|
|
|
# This is to avoid local_fields problems for proxy models. Refs #17717.
|
2012-03-04 05:34:51 +08:00
|
|
|
concrete_model = obj._meta.concrete_model
|
|
|
|
for field in concrete_model._meta.local_fields:
|
2007-03-19 19:57:53 +08:00
|
|
|
if field.serialize:
|
2015-02-26 22:19:17 +08:00
|
|
|
if field.remote_field is None:
|
2007-03-19 19:57:53 +08:00
|
|
|
if self.selected_fields is None or field.attname in self.selected_fields:
|
|
|
|
self.handle_field(obj, field)
|
|
|
|
else:
|
|
|
|
if self.selected_fields is None or field.attname[:-3] in self.selected_fields:
|
|
|
|
self.handle_fk_field(obj, field)
|
2012-03-04 05:34:51 +08:00
|
|
|
for field in concrete_model._meta.many_to_many:
|
2007-03-19 19:57:53 +08:00
|
|
|
if field.serialize:
|
|
|
|
if self.selected_fields is None or field.attname in self.selected_fields:
|
|
|
|
self.handle_m2m_field(obj, field)
|
2006-06-29 00:00:37 +08:00
|
|
|
self.end_object(obj)
|
2012-05-26 17:43:37 +08:00
|
|
|
if self.first:
|
|
|
|
self.first = False
|
2006-06-29 00:00:37 +08:00
|
|
|
self.end_serialization()
|
|
|
|
return self.getvalue()
|
2006-08-31 12:11:46 +08:00
|
|
|
|
2006-06-29 00:00:37 +08:00
|
|
|
def start_serialization(self):
|
|
|
|
"""
|
|
|
|
Called when serializing of the queryset starts.
|
|
|
|
"""
|
2013-09-07 02:24:52 +08:00
|
|
|
raise NotImplementedError('subclasses of Serializer must provide a start_serialization() method')
|
2006-08-31 12:11:46 +08:00
|
|
|
|
2006-06-29 00:00:37 +08:00
|
|
|
def end_serialization(self):
|
|
|
|
"""
|
|
|
|
Called when serializing of the queryset ends.
|
|
|
|
"""
|
|
|
|
pass
|
2006-08-31 12:11:46 +08:00
|
|
|
|
2006-06-29 00:00:37 +08:00
|
|
|
def start_object(self, obj):
|
|
|
|
"""
|
|
|
|
Called when serializing of an object starts.
|
|
|
|
"""
|
2013-09-07 02:24:52 +08:00
|
|
|
raise NotImplementedError('subclasses of Serializer must provide a start_object() method')
|
2006-08-31 12:11:46 +08:00
|
|
|
|
2006-06-29 00:00:37 +08:00
|
|
|
def end_object(self, obj):
|
|
|
|
"""
|
|
|
|
Called when serializing of an object ends.
|
|
|
|
"""
|
|
|
|
pass
|
2006-08-31 12:11:46 +08:00
|
|
|
|
2006-06-29 00:00:37 +08:00
|
|
|
def handle_field(self, obj, field):
|
|
|
|
"""
|
|
|
|
Called to handle each individual (non-relational) field on an object.
|
|
|
|
"""
|
2013-09-07 02:24:52 +08:00
|
|
|
raise NotImplementedError('subclasses of Serializer must provide an handle_field() method')
|
2006-08-31 12:11:46 +08:00
|
|
|
|
2006-06-29 00:00:37 +08:00
|
|
|
def handle_fk_field(self, obj, field):
|
|
|
|
"""
|
|
|
|
Called to handle a ForeignKey field.
|
|
|
|
"""
|
2013-09-07 02:24:52 +08:00
|
|
|
raise NotImplementedError('subclasses of Serializer must provide an handle_fk_field() method')
|
2006-08-31 12:11:46 +08:00
|
|
|
|
2006-06-29 00:00:37 +08:00
|
|
|
def handle_m2m_field(self, obj, field):
|
|
|
|
"""
|
|
|
|
Called to handle a ManyToManyField.
|
|
|
|
"""
|
2013-09-07 02:24:52 +08:00
|
|
|
raise NotImplementedError('subclasses of Serializer must provide an handle_m2m_field() method')
|
2006-08-31 12:11:46 +08:00
|
|
|
|
2006-06-29 00:00:37 +08:00
|
|
|
def getvalue(self):
|
|
|
|
"""
|
2007-04-25 18:12:05 +08:00
|
|
|
Return the fully serialized queryset (or None if the output stream is
|
|
|
|
not seekable).
|
2006-06-29 00:00:37 +08:00
|
|
|
"""
|
2007-04-25 18:12:05 +08:00
|
|
|
if callable(getattr(self.stream, 'getvalue', None)):
|
|
|
|
return self.stream.getvalue()
|
2006-06-29 00:00:37 +08:00
|
|
|
|
2013-11-03 04:12:09 +08:00
|
|
|
|
2012-11-04 04:43:11 +08:00
|
|
|
class Deserializer(six.Iterator):
|
2006-06-29 00:00:37 +08:00
|
|
|
"""
|
|
|
|
Abstract base deserializer class.
|
|
|
|
"""
|
2006-08-31 12:11:46 +08:00
|
|
|
|
2006-06-29 00:00:37 +08:00
|
|
|
def __init__(self, stream_or_string, **options):
|
|
|
|
"""
|
|
|
|
Init this serializer given a stream or a string
|
|
|
|
"""
|
|
|
|
self.options = options
|
2012-07-20 20:22:00 +08:00
|
|
|
if isinstance(stream_or_string, six.string_types):
|
2012-08-15 01:48:38 +08:00
|
|
|
self.stream = six.StringIO(stream_or_string)
|
2006-06-29 00:00:37 +08:00
|
|
|
else:
|
|
|
|
self.stream = stream_or_string
|
2006-08-31 12:11:46 +08:00
|
|
|
|
2006-06-29 00:00:37 +08:00
|
|
|
def __iter__(self):
|
|
|
|
return self
|
2006-08-31 12:11:46 +08:00
|
|
|
|
2012-08-09 20:36:05 +08:00
|
|
|
def __next__(self):
|
2006-06-29 00:00:37 +08:00
|
|
|
"""Iteration iterface -- return the next item in the stream"""
|
2013-09-07 02:24:52 +08:00
|
|
|
raise NotImplementedError('subclasses of Deserializer must provide a __next__() method')
|
2006-08-31 12:11:46 +08:00
|
|
|
|
2013-11-03 04:12:09 +08:00
|
|
|
|
2006-06-29 00:00:37 +08:00
|
|
|
class DeserializedObject(object):
|
|
|
|
"""
|
2007-03-01 21:11:08 +08:00
|
|
|
A deserialized model.
|
2006-08-31 12:11:46 +08:00
|
|
|
|
2006-06-29 00:00:37 +08:00
|
|
|
Basically a container for holding the pre-saved deserialized data along
|
|
|
|
with the many-to-many data saved with the object.
|
2006-08-31 12:11:46 +08:00
|
|
|
|
2006-06-29 00:00:37 +08:00
|
|
|
Call ``save()`` to save the object (with the many-to-many data) to the
|
|
|
|
database; call ``save(save_m2m=False)`` to save just the object fields
|
|
|
|
(and not touch the many-to-many stuff.)
|
|
|
|
"""
|
2006-08-31 12:11:46 +08:00
|
|
|
|
2006-06-29 00:00:37 +08:00
|
|
|
def __init__(self, obj, m2m_data=None):
|
|
|
|
self.object = obj
|
|
|
|
self.m2m_data = m2m_data
|
2006-08-31 12:11:46 +08:00
|
|
|
|
2006-06-29 00:00:37 +08:00
|
|
|
def __repr__(self):
|
2015-04-27 05:05:50 +08:00
|
|
|
return "<DeserializedObject: %s(pk=%s)>" % (
|
|
|
|
self.object._meta.label, self.object.pk)
|
2006-08-31 12:11:46 +08:00
|
|
|
|
2015-06-05 22:58:36 +08:00
|
|
|
def save(self, save_m2m=True, using=None, **kwargs):
|
2008-03-18 22:35:05 +08:00
|
|
|
# Call save on the Model baseclass directly. This bypasses any
|
2007-07-12 15:45:35 +08:00
|
|
|
# model-defined save. The save is also forced to be raw.
|
2013-05-12 07:34:02 +08:00
|
|
|
# raw=True is passed to any pre/post_save signals.
|
2015-06-05 22:58:36 +08:00
|
|
|
models.Model.save_base(self.object, using=using, raw=True, **kwargs)
|
2006-06-29 00:00:37 +08:00
|
|
|
if self.m2m_data and save_m2m:
|
|
|
|
for accessor_name, object_list in self.m2m_data.items():
|
|
|
|
setattr(self.object, accessor_name, object_list)
|
2006-08-31 12:11:46 +08:00
|
|
|
|
|
|
|
# prevent a second (possibly accidental) call to save() from saving
|
2006-06-29 00:00:37 +08:00
|
|
|
# the m2m data twice.
|
|
|
|
self.m2m_data = None
|
2012-08-01 09:49:01 +08:00
|
|
|
|
2013-11-03 04:12:09 +08:00
|
|
|
|
2012-08-01 09:49:01 +08:00
|
|
|
def build_instance(Model, data, db):
|
|
|
|
"""
|
|
|
|
Build a model instance.
|
|
|
|
|
|
|
|
If the model instance doesn't have a primary key and the model supports
|
|
|
|
natural keys, try to retrieve it from the database.
|
|
|
|
"""
|
|
|
|
obj = Model(**data)
|
|
|
|
if (obj.pk is None and hasattr(Model, 'natural_key') and
|
|
|
|
hasattr(Model._default_manager, 'get_by_natural_key')):
|
|
|
|
natural_key = obj.natural_key()
|
|
|
|
try:
|
|
|
|
obj.pk = Model._default_manager.db_manager(db).get_by_natural_key(*natural_key).pk
|
|
|
|
except Model.DoesNotExist:
|
|
|
|
pass
|
|
|
|
return obj
|