Permalink
Browse files

Added initial cut at serialization framework, along with some basic t…

…ests and a stab at some docs. This is all a bit rough right now, so expect some bumps.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@3225 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
jacobian committed Jun 28, 2006
1 parent 414bc24 commit 4ea7a11659b8a0ab07b0d2e847975f7324664f10
@@ -0,0 +1,76 @@
+"""
+Interfaces for serializing Django objects.
+
+Usage::
+
+ >>> from django.core import serializers
+ >>> json = serializers.serialize("json", some_query_set)
+ >>> objects = list(serializers.deserialize("json", json))
+
+To add your own serializers, use the SERIALIZATION_MODULES setting::
+
+ SERIALIZATION_MODULES = {
+ "csv" : "path.to.csv.serializer",
+ "txt" : "path.to.txt.serializer",
+ }
+
+"""
+
+from django.conf import settings
+
+# Built-in serializers
+BUILTIN_SERIALIZERS = {
+ "xml" : "django.core.serializers.xml_serializer",
+}
+
+_serializers = {}
+
+def register_serializer(format, serializer_module):
+ """Register a new serializer by passing in a module name."""
+ module = __import__(serializer_module, '', '', [''])
+ _serializers[format] = module
+
+def unregister_serializer(format):
+ """Unregister a given serializer"""
+ del _serializers[format]
+
+def get_serializer(format):
+ if not _serializers:
+ _load_serializers()
+ return _serializers[format].Serializer
+
+def get_deserializer(format):
+ if not _serializers:
+ _load_serializers()
+ return _serializers[format].Deserializer
+
+def serialize(format, queryset, **options):
+ """
+ Serialize a queryset (or any iterator that returns database objects) using
+ a certain serializer.
+ """
+ s = get_serializer(format)()
+ s.serialize(queryset, **options)
+ return s.getvalue()
+
+def deserialize(format, stream_or_string):
+ """
+ Deserialize a stream or a string. Returns an iterator that yields ``(obj,
+ m2m_relation_dict)``, where ``obj`` is a instantiated -- but *unsaved* --
+ object, and ``m2m_relation_dict`` is a dictionary of ``{m2m_field_name :
+ list_of_related_objects}``.
+ """
+ d = get_deserializer(format)
+ return d(stream_or_string)
+
+def _load_serializers():
+ """
+ Register built-in and settings-defined serializers. This is done lazily so
+ that user code has a chance to (e.g.) set up custom settings without
+ needing to be careful of import order.
+ """
+ for format in BUILTIN_SERIALIZERS:
+ register_serializer(format, BUILTIN_SERIALIZERS[format])
+ if hasattr(settings, "SERIALIZATION_MODULES"):
+ for format in settings.SERIALIZATION_MODULES:
+ register_serializer(format, settings.SERIALIZATION_MODULES[format])
@@ -0,0 +1,159 @@
+"""
+Module for abstract serializer/unserializer base classes.
+"""
+
+try:
+ from cStringIO import StringIO
+except ImportError:
+ from StringIO import StringIO
+from django.db import models
+
+class SerializationError(Exception):
+ """Something bad happened during serialization."""
+ pass
+
+class DeserializationError(Exception):
+ """Something bad happened during deserialization."""
+ pass
+
+class Serializer(object):
+ """
+ Abstract serializer base class.
+ """
+
+ def serialize(self, queryset, **options):
+ """
+ Serialize a queryset.
+ """
+ self.options = options
+
+ self.stream = options.get("stream", StringIO())
+
+ self.start_serialization()
+ for obj in queryset:
+ self.start_object(obj)
+ for field in obj._meta.fields:
+ if field.rel is None:
+ self.handle_field(obj, field)
+ else:
+ self.handle_fk_field(obj, field)
+ for field in obj._meta.many_to_many:
+ self.handle_m2m_field(obj, field)
+ self.end_object(obj)
+ self.end_serialization()
+ return self.getvalue()
+
+ def get_string_value(self, obj, field):
+ """
+ Convert a field's value to a string.
+ """
+ if isinstance(field, models.DateTimeField):
+ value = getattr(obj, field.name).strftime("%Y-%m-%d %H:%M:%S")
+ elif isinstance(field, models.FileField):
+ value = getattr(obj, "get_%s_url" % field.name, lambda: None)()
+ else:
+ value = field.flatten_data(follow=None, obj=obj).get(field.name, "")
+ return str(value)
+
+ def start_serialization(self):
+ """
+ Called when serializing of the queryset starts.
+ """
+ raise NotImplementedError
+
+ def end_serialization(self):
+ """
+ Called when serializing of the queryset ends.
+ """
+ pass
+
+ def start_object(self, obj):
+ """
+ Called when serializing of an object starts.
+ """
+ raise NotImplementedError
+
+ def end_object(self, obj):
+ """
+ Called when serializing of an object ends.
+ """
+ pass
+
+ def handle_field(self, obj, field):
+ """
+ Called to handle each individual (non-relational) field on an object.
+ """
+ raise NotImplementedError
+
+ def handle_fk_field(self, obj, field):
+ """
+ Called to handle a ForeignKey field.
+ """
+ raise NotImplementedError
+
+ def handle_m2m_field(self, obj, field):
+ """
+ Called to handle a ManyToManyField.
+ """
+ raise NotImplementedError
+
+ def getvalue(self):
+ """
+ Return the fully serialized queryset.
+ """
+ return self.stream.getvalue()
+
+class Deserializer(object):
+ """
+ Abstract base deserializer class.
+ """
+
+ def __init__(self, stream_or_string, **options):
+ """
+ Init this serializer given a stream or a string
+ """
+ self.options = options
+ if isinstance(stream_or_string, basestring):
+ self.stream = StringIO(stream_or_string)
+ else:
+ self.stream = stream_or_string
+ # hack to make sure that the models have all been loaded before
+ # deserialization starts (otherwise subclass calls to get_model()
+ # and friends might fail...)
+ models.get_apps()
+
+ def __iter__(self):
+ return self
+
+ def next(self):
+ """Iteration iterface -- return the next item in the stream"""
+ raise NotImplementedError
+
+class DeserializedObject(object):
+ """
+ A deserialzed model.
+
+ Basically a container for holding the pre-saved deserialized data along
+ with the many-to-many data saved with the object.
+
+ 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.)
+ """
+
+ def __init__(self, obj, m2m_data=None):
+ self.object = obj
+ self.m2m_data = m2m_data
+
+ def __repr__(self):
+ return "<DeserializedObject: %s>" % str(self.object)
+
+ def save(self, save_m2m=True):
+ self.object.save()
+ if self.m2m_data and save_m2m:
+ for accessor_name, object_list in self.m2m_data.items():
+ setattr(self.object, accessor_name, object_list)
+
+ # prevent a second (possibly accidental) call to save() from saving
+ # the m2m data twice.
+ self.m2m_data = None
Oops, something went wrong.

0 comments on commit 4ea7a11

Please sign in to comment.