Skip to content

Commit

Permalink
Added initial cut at serialization framework, along with some basic t…
Browse files Browse the repository at this point in the history
…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 4ea7a11
Show file tree
Hide file tree
Showing 6 changed files with 632 additions and 0 deletions.
76 changes: 76 additions & 0 deletions django/core/serializers/__init__.py
@@ -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])
159 changes: 159 additions & 0 deletions django/core/serializers/base.py
@@ -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

0 comments on commit 4ea7a11

Please sign in to comment.