summaryrefslogtreecommitdiff
path: root/django/core/serializers
diff options
context:
space:
mode:
authorChristopher Long <indirecthit@gmail.com>2007-06-17 22:18:54 +0000
committerChristopher Long <indirecthit@gmail.com>2007-06-17 22:18:54 +0000
commitae22b6d403dcf25098c77f0dfcf59ae58b186461 (patch)
treec37fc631e99a7e4d909d6b6d236f495003731ea7 /django/core/serializers
parent0cf7bc439129c66df8d64601e885f83b256b4f25 (diff)
per-object-permissions: Merged to trunk [5486] NOTE: Not fully tested, will be working on this over the next few weeks.
git-svn-id: http://code.djangoproject.com/svn/django/branches/per-object-permissions@5488 bcc190cf-cafb-0310-a4f2-bffc1f526a37
Diffstat (limited to 'django/core/serializers')
-rw-r--r--django/core/serializers/__init__.py12
-rw-r--r--django/core/serializers/base.py34
-rw-r--r--django/core/serializers/json.py34
-rw-r--r--django/core/serializers/python.py28
-rw-r--r--django/core/serializers/pyyaml.py38
-rw-r--r--django/core/serializers/xml_serializer.py58
6 files changed, 144 insertions, 60 deletions
diff --git a/django/core/serializers/__init__.py b/django/core/serializers/__init__.py
index a1268321f2..494393f3cf 100644
--- a/django/core/serializers/__init__.py
+++ b/django/core/serializers/__init__.py
@@ -25,6 +25,13 @@ BUILTIN_SERIALIZERS = {
"json" : "django.core.serializers.json",
}
+# Check for PyYaml and register the serializer if it's available.
+try:
+ import yaml
+ BUILTIN_SERIALIZERS["yaml"] = "django.core.serializers.pyyaml"
+except ImportError:
+ pass
+
_serializers = {}
def register_serializer(format, serializer_module):
@@ -40,6 +47,11 @@ def get_serializer(format):
if not _serializers:
_load_serializers()
return _serializers[format].Serializer
+
+def get_serializer_formats():
+ if not _serializers:
+ _load_serializers()
+ return _serializers.keys()
def get_deserializer(format):
if not _serializers:
diff --git a/django/core/serializers/base.py b/django/core/serializers/base.py
index 5b0acdc480..86d0037c17 100644
--- a/django/core/serializers/base.py
+++ b/django/core/serializers/base.py
@@ -34,17 +34,17 @@ class Serializer(object):
for obj in queryset:
self.start_object(obj)
for field in obj._meta.fields:
- if field is obj._meta.pk:
- continue
- elif field.rel is None:
- 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)
+ if field.serialize:
+ if field.rel is None:
+ 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)
for field in obj._meta.many_to_many:
- if self.selected_fields is None or field.attname in self.selected_fields:
- self.handle_m2m_field(obj, field)
+ if field.serialize:
+ if self.selected_fields is None or field.attname in self.selected_fields:
+ self.handle_m2m_field(obj, field)
self.end_object(obj)
self.end_serialization()
return self.getvalue()
@@ -54,11 +54,7 @@ class Serializer(object):
Convert a field's value to a string.
"""
if isinstance(field, models.DateTimeField):
- value = getattr(obj, field.name)
- if value is None:
- value = ''
- else:
- value = value.strftime("%Y-%m-%d %H:%M:%S")
+ 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:
@@ -109,9 +105,11 @@ class Serializer(object):
def getvalue(self):
"""
- Return the fully serialized queryset.
+ Return the fully serialized queryset (or None if the output stream is
+ not seekable).
"""
- return self.stream.getvalue()
+ if callable(getattr(self.stream, 'getvalue', None)):
+ return self.stream.getvalue()
class Deserializer(object):
"""
@@ -141,7 +139,7 @@ class Deserializer(object):
class DeserializedObject(object):
"""
- A deserialzed model.
+ A deserialized model.
Basically a container for holding the pre-saved deserialized data along
with the many-to-many data saved with the object.
diff --git a/django/core/serializers/json.py b/django/core/serializers/json.py
index 15770f160e..fa2dca7295 100644
--- a/django/core/serializers/json.py
+++ b/django/core/serializers/json.py
@@ -4,22 +4,30 @@ Serialize data to/from JSON
import datetime
from django.utils import simplejson
+from django.utils.simplejson import decoder
from django.core.serializers.python import Serializer as PythonSerializer
from django.core.serializers.python import Deserializer as PythonDeserializer
try:
from cStringIO import StringIO
except ImportError:
from StringIO import StringIO
+try:
+ import decimal
+except ImportError:
+ from django.utils import _decimal as decimal # Python 2.3 fallback
class Serializer(PythonSerializer):
"""
Convert a queryset to JSON.
"""
def end_serialization(self):
- simplejson.dump(self.objects, self.stream, cls=DateTimeAwareJSONEncoder, **self.options)
-
+ self.options.pop('stream', None)
+ self.options.pop('fields', None)
+ simplejson.dump(self.objects, self.stream, cls=DjangoJSONEncoder, **self.options)
+
def getvalue(self):
- return self.stream.getvalue()
+ if callable(getattr(self.stream, 'getvalue', None)):
+ return self.stream.getvalue()
def Deserializer(stream_or_string, **options):
"""
@@ -31,15 +39,15 @@ def Deserializer(stream_or_string, **options):
stream = stream_or_string
for obj in PythonDeserializer(simplejson.load(stream)):
yield obj
-
-class DateTimeAwareJSONEncoder(simplejson.JSONEncoder):
+
+class DjangoJSONEncoder(simplejson.JSONEncoder):
"""
- JSONEncoder subclass that knows how to encode date/time types
+ JSONEncoder subclass that knows how to encode date/time and decimal types.
"""
-
- DATE_FORMAT = "%Y-%m-%d"
+
+ DATE_FORMAT = "%Y-%m-%d"
TIME_FORMAT = "%H:%M:%S"
-
+
def default(self, o):
if isinstance(o, datetime.datetime):
return o.strftime("%s %s" % (self.DATE_FORMAT, self.TIME_FORMAT))
@@ -47,5 +55,11 @@ class DateTimeAwareJSONEncoder(simplejson.JSONEncoder):
return o.strftime(self.DATE_FORMAT)
elif isinstance(o, datetime.time):
return o.strftime(self.TIME_FORMAT)
+ elif isinstance(o, decimal.Decimal):
+ return str(o)
else:
- return super(DateTimeAwareJSONEncoder, self).default(o)
+ return super(DjangoJSONEncoder, self).default(o)
+
+# Older, deprecated class name (for backwards compatibility purposes).
+DateTimeAwareJSONEncoder = DjangoJSONEncoder
+
diff --git a/django/core/serializers/python.py b/django/core/serializers/python.py
index 859816c226..5fbb3163f7 100644
--- a/django/core/serializers/python.py
+++ b/django/core/serializers/python.py
@@ -37,7 +37,12 @@ class Serializer(base.Serializer):
def handle_fk_field(self, obj, field):
related = getattr(obj, field.name)
if related is not None:
- related = related._get_pk_val()
+ if field.rel.field_name == related._meta.pk.name:
+ # Related to remote object via primary key
+ related = related._get_pk_val()
+ else:
+ # Related to remote object via other field
+ related = getattr(related, field.rel.field_name)
self._current[field.name] = related
def handle_m2m_field(self, obj, field):
@@ -57,7 +62,7 @@ def Deserializer(object_list, **options):
for d in object_list:
# Look up the model and starting build a dict of data for it.
Model = _get_model(d["model"])
- data = {Model._meta.pk.name : d["pk"]}
+ data = {Model._meta.pk.attname : Model._meta.pk.to_python(d["pk"])}
m2m_data = {}
# Handle each field
@@ -67,20 +72,23 @@ def Deserializer(object_list, **options):
field = Model._meta.get_field(field_name)
- # Handle M2M relations (with in_bulk() for performance)
+ # Handle M2M relations
if field.rel and isinstance(field.rel, models.ManyToManyRel):
pks = []
+ m2m_convert = field.rel.to._meta.pk.to_python
for pk in field_value:
if isinstance(pk, unicode):
- pk = pk.encode(options.get("encoding", settings.DEFAULT_CHARSET))
- m2m_data[field.name] = field.rel.to._default_manager.in_bulk(field_value).values()
+ pks.append(m2m_convert(pk.encode(options.get("encoding", settings.DEFAULT_CHARSET))))
+ else:
+ pks.append(m2m_convert(pk))
+ m2m_data[field.name] = pks
# Handle FK fields
- elif field.rel and isinstance(field.rel, models.ManyToOneRel) and field_value is not None:
- try:
- data[field.name] = field.rel.to._default_manager.get(pk=field_value)
- except field.rel.to.DoesNotExist:
- data[field.name] = None
+ elif field.rel and isinstance(field.rel, models.ManyToOneRel):
+ if field_value:
+ data[field.attname] = field.rel.to._meta.get_field(field.rel.field_name).to_python(field_value)
+ else:
+ data[field.attname] = None
# Handle all other fields
else:
diff --git a/django/core/serializers/pyyaml.py b/django/core/serializers/pyyaml.py
new file mode 100644
index 0000000000..d3444280c5
--- /dev/null
+++ b/django/core/serializers/pyyaml.py
@@ -0,0 +1,38 @@
+"""
+YAML serializer.
+
+Requires PyYaml (http://pyyaml.org/), but that's checked for in __init__.
+"""
+
+import datetime
+from django.core.serializers.python import Serializer as PythonSerializer
+from django.core.serializers.python import Deserializer as PythonDeserializer
+try:
+ from cStringIO import StringIO
+except ImportError:
+ from StringIO import StringIO
+import yaml
+
+class Serializer(PythonSerializer):
+ """
+ Convert a queryset to YAML.
+ """
+ def end_serialization(self):
+ self.options.pop('stream', None)
+ self.options.pop('fields', None)
+ yaml.dump(self.objects, self.stream, **self.options)
+
+ def getvalue(self):
+ return self.stream.getvalue()
+
+def Deserializer(stream_or_string, **options):
+ """
+ Deserialize a stream or string of YAML data.
+ """
+ if isinstance(stream_or_string, basestring):
+ stream = StringIO(stream_or_string)
+ else:
+ stream = stream_or_string
+ for obj in PythonDeserializer(yaml.load(stream)):
+ yield obj
+
diff --git a/django/core/serializers/xml_serializer.py b/django/core/serializers/xml_serializer.py
index 512b8c6176..3e4a6f3e79 100644
--- a/django/core/serializers/xml_serializer.py
+++ b/django/core/serializers/xml_serializer.py
@@ -13,6 +13,10 @@ class Serializer(base.Serializer):
Serializes a QuerySet to XML.
"""
+ def indent(self, level):
+ if self.options.get('indent', None) is not None:
+ self.xml.ignorableWhitespace('\n' + ' ' * self.options.get('indent', None) * level)
+
def start_serialization(self):
"""
Start serialization -- open the XML document and the root element.
@@ -25,6 +29,7 @@ class Serializer(base.Serializer):
"""
End serialization -- end the document.
"""
+ self.indent(0)
self.xml.endElement("django-objects")
self.xml.endDocument()
@@ -35,6 +40,7 @@ class Serializer(base.Serializer):
if not hasattr(obj, "_meta"):
raise base.SerializationError("Non-model object (%s) encountered during serialization" % type(obj))
+ self.indent(1)
self.xml.startElement("object", {
"pk" : str(obj._get_pk_val()),
"model" : str(obj._meta),
@@ -44,6 +50,7 @@ class Serializer(base.Serializer):
"""
Called after handling all fields for an object.
"""
+ self.indent(1)
self.xml.endElement("object")
def handle_field(self, obj, field):
@@ -51,16 +58,19 @@ class Serializer(base.Serializer):
Called to handle each field on an object (except for ForeignKeys and
ManyToManyFields)
"""
+ self.indent(2)
self.xml.startElement("field", {
"name" : field.name,
"type" : field.get_internal_type()
})
# Get a "string version" of the object's data (this is handled by the
- # serializer base class). None is handled specially.
- value = self.get_string_value(obj, field)
- if value is not None:
+ # serializer base class).
+ if getattr(obj, field.name) is not None:
+ value = self.get_string_value(obj, field)
self.xml.characters(str(value))
+ else:
+ self.xml.addQuickElement("None")
self.xml.endElement("field")
@@ -72,7 +82,13 @@ class Serializer(base.Serializer):
self._start_relational_field(field)
related = getattr(obj, field.name)
if related is not None:
- self.xml.characters(str(related._get_pk_val()))
+ if field.rel.field_name == related._meta.pk.name:
+ # Related to remote object via primary key
+ related = related._get_pk_val()
+ else:
+ # Related to remote object via other field
+ related = getattr(related, field.rel.field_name)
+ self.xml.characters(str(related))
else:
self.xml.addQuickElement("None")
self.xml.endElement("field")
@@ -92,6 +108,7 @@ class Serializer(base.Serializer):
"""
Helper to output the <field> element for relational fields
"""
+ self.indent(2)
self.xml.startElement("field", {
"name" : field.name,
"rel" : field.rel.__class__.__name__,
@@ -127,7 +144,8 @@ class Deserializer(base.Deserializer):
pk = node.getAttribute("pk")
if not pk:
raise base.DeserializationError("<object> node is missing the 'pk' attribute")
- data = {Model._meta.pk.name : pk}
+
+ data = {Model._meta.pk.attname : Model._meta.pk.to_python(pk)}
# Also start building a dict of m2m data (this is saved as
# {m2m_accessor_attribute : [list_of_related_objects]})
@@ -148,41 +166,37 @@ class Deserializer(base.Deserializer):
# As is usually the case, relation fields get the special treatment.
if field.rel and isinstance(field.rel, models.ManyToManyRel):
- m2m_data[field.name] = self._handle_m2m_field_node(field_node)
+ m2m_data[field.name] = self._handle_m2m_field_node(field_node, field)
elif field.rel and isinstance(field.rel, models.ManyToOneRel):
- data[field.name] = self._handle_fk_field_node(field_node)
+ data[field.attname] = self._handle_fk_field_node(field_node, field)
else:
- value = field.to_python(getInnerText(field_node).strip().encode(self.encoding))
+ if len(field_node.childNodes) == 1 and field_node.childNodes[0].nodeName == 'None':
+ value = None
+ else:
+ value = field.to_python(getInnerText(field_node).strip().encode(self.encoding))
data[field.name] = value
# Return a DeserializedObject so that the m2m data has a place to live.
return base.DeserializedObject(Model(**data), m2m_data)
- def _handle_fk_field_node(self, node):
+ def _handle_fk_field_node(self, node, field):
"""
Handle a <field> node for a ForeignKey
"""
- # Try to set the foreign key by looking up the foreign related object.
- # If it doesn't exist, set the field to None (which might trigger
- # validation error, but that's expected).
- RelatedModel = self._get_model_from_node(node, "to")
# Check if there is a child node named 'None', returning None if so.
if len(node.childNodes) == 1 and node.childNodes[0].nodeName == 'None':
return None
else:
- return RelatedModel.objects.get(pk=getInnerText(node).strip().encode(self.encoding))
+ return field.rel.to._meta.get_field(field.rel.field_name).to_python(
+ getInnerText(node).strip().encode(self.encoding))
- def _handle_m2m_field_node(self, node):
+ def _handle_m2m_field_node(self, node, field):
"""
Handle a <field> node for a ManyToManyField
"""
- # Load the related model
- RelatedModel = self._get_model_from_node(node, "to")
-
- # Look up all the related objects. Using the in_bulk() lookup ensures
- # that missing related objects don't cause an exception
- related_ids = [c.getAttribute("pk").encode(self.encoding) for c in node.getElementsByTagName("object")]
- return RelatedModel._default_manager.in_bulk(related_ids).values()
+ return [field.rel.to._meta.pk.to_python(
+ c.getAttribute("pk").encode(self.encoding))
+ for c in node.getElementsByTagName("object")]
def _get_model_from_node(self, node, attr):
"""