summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRussell Keith-Magee <russell@keith-magee.com>2009-04-13 12:35:49 +0000
committerRussell Keith-Magee <russell@keith-magee.com>2009-04-13 12:35:49 +0000
commitcb43898d49ac60bf84a2eb83b331072e7651af5e (patch)
tree1dfed754b16d60b3791feaca9d420010864b72ba
parentfd3ee7d7867dd13a336b90873ccb8df6302f2c05 (diff)
Fixed #9522 -- Modified handling of values in base serializer so that field subclasses can define their own value_to_string() method for serialization. Thanks to Alex Koshelev for the report and initial patch.
git-svn-id: http://code.djangoproject.com/svn/django/trunk@10554 bcc190cf-cafb-0310-a4f2-bffc1f526a37
-rw-r--r--django/core/serializers/python.py15
-rw-r--r--django/core/serializers/xml_serializer.py6
-rw-r--r--django/utils/encoding.py15
-rw-r--r--tests/modeltests/serializers/models.py68
4 files changed, 95 insertions, 9 deletions
diff --git a/django/core/serializers/python.py b/django/core/serializers/python.py
index 0eeb485797..b672e4efc3 100644
--- a/django/core/serializers/python.py
+++ b/django/core/serializers/python.py
@@ -7,15 +7,15 @@ other serializers.
from django.conf import settings
from django.core.serializers import base
from django.db import models
-from django.utils.encoding import smart_unicode
+from django.utils.encoding import smart_unicode, is_protected_type
class Serializer(base.Serializer):
"""
Serializes a QuerySet to basic Python objects.
"""
-
+
internal_use_only = True
-
+
def start_serialization(self):
self._current = None
self.objects = []
@@ -35,7 +35,14 @@ class Serializer(base.Serializer):
self._current = None
def handle_field(self, obj, field):
- self._current[field.name] = smart_unicode(getattr(obj, field.name), strings_only=True)
+ value = field._get_val_from_obj(obj)
+ # Protected types (i.e., primitives like None, numbers, dates,
+ # and Decimals) are passed through as is. All other values are
+ # converted to string first.
+ if is_protected_type(value):
+ self._current[field.name] = value
+ else:
+ self._current[field.name] = field.value_to_string(obj)
def handle_fk_field(self, obj, field):
related = getattr(obj, field.name)
diff --git a/django/core/serializers/xml_serializer.py b/django/core/serializers/xml_serializer.py
index 04498db00c..2d74fe28f3 100644
--- a/django/core/serializers/xml_serializer.py
+++ b/django/core/serializers/xml_serializer.py
@@ -65,11 +65,9 @@ class Serializer(base.Serializer):
"type" : field.get_internal_type()
})
- # Get a "string version" of the object's data (this is handled by the
- # serializer base class).
+ # Get a "string version" of the object's data.
if getattr(obj, field.name) is not None:
- value = self.get_string_value(obj, field)
- self.xml.characters(smart_unicode(value))
+ self.xml.characters(field.value_to_string(obj))
else:
self.xml.addQuickElement("None")
diff --git a/django/utils/encoding.py b/django/utils/encoding.py
index 7e87b05c79..9388d67f4c 100644
--- a/django/utils/encoding.py
+++ b/django/utils/encoding.py
@@ -41,6 +41,19 @@ def smart_unicode(s, encoding='utf-8', strings_only=False, errors='strict'):
return s
return force_unicode(s, encoding, strings_only, errors)
+def is_protected_type(obj):
+ """Determine if the object instance is of a protected type.
+
+ Objects of protected types are preserved as-is when passed to
+ force_unicode(strings_only=True).
+ """
+ return isinstance(obj, (
+ types.NoneType,
+ int, long,
+ datetime.datetime, datetime.date, datetime.time,
+ float, Decimal)
+ )
+
def force_unicode(s, encoding='utf-8', strings_only=False, errors='strict'):
"""
Similar to smart_unicode, except that lazy instances are resolved to
@@ -48,7 +61,7 @@ def force_unicode(s, encoding='utf-8', strings_only=False, errors='strict'):
If strings_only is True, don't convert (some) non-string-like objects.
"""
- if strings_only and isinstance(s, (types.NoneType, int, long, datetime.datetime, datetime.date, datetime.time, float, Decimal)):
+ if strings_only and is_protected_type(s):
return s
try:
if not isinstance(s, basestring,):
diff --git a/tests/modeltests/serializers/models.py b/tests/modeltests/serializers/models.py
index eda19ecc4f..cf1bf50c24 100644
--- a/tests/modeltests/serializers/models.py
+++ b/tests/modeltests/serializers/models.py
@@ -73,6 +73,45 @@ class Movie(models.Model):
class Score(models.Model):
score = models.FloatField()
+
+class Team(object):
+ def __init__(self, title):
+ self.title = title
+
+ def __unicode__(self):
+ raise NotImplementedError("Not so simple")
+
+ def __str__(self):
+ raise NotImplementedError("Not so simple")
+
+ def to_string(self):
+ return "%s" % self.title
+
+class TeamField(models.CharField):
+ __metaclass__ = models.SubfieldBase
+
+ def __init__(self):
+ super(TeamField, self).__init__(max_length=100)
+
+ def get_db_prep_save(self, value):
+ return unicode(value.title)
+
+ def to_python(self, value):
+ if isinstance(value, Team):
+ return value
+ return Team(value)
+
+ def value_to_string(self, obj):
+ return self._get_val_from_obj(obj).to_string()
+
+class Player(models.Model):
+ name = models.CharField(max_length=50)
+ rank = models.IntegerField()
+ team = TeamField()
+
+ def __unicode__(self):
+ return u'%s (%d) playing for %s' % (self.name, self.rank, self.team.to_string())
+
__test__ = {'API_TESTS':"""
# Create some data:
>>> from datetime import datetime
@@ -223,6 +262,21 @@ None
>>> print list(serializers.deserialize('json', serializers.serialize('json', [sc])))[0].object.score
3.4
+# Custom field with non trivial to string convertion value
+>>> player = Player()
+>>> player.name = "Soslan Djanaev"
+>>> player.rank = 1
+>>> player.team = Team("Spartak Moskva")
+>>> player.save()
+
+>>> serialized = serializers.serialize("json", Player.objects.all())
+>>> print serialized
+[{"pk": 1, "model": "serializers.player", "fields": {"name": "Soslan Djanaev", "rank": 1, "team": "Spartak Moskva"}}]
+
+>>> obj = list(serializers.deserialize("json", serialized))[0]
+>>> print obj
+<DeserializedObject: Soslan Djanaev (1) playing for Spartak Moskva>
+
"""}
try:
@@ -259,6 +313,20 @@ try:
<DeserializedObject: Just kidding; I love TV poker>
<DeserializedObject: Time to reform copyright>
+# Custom field with non trivial to string convertion value with YAML serializer
+
+>>> print serializers.serialize("yaml", Player.objects.all())
+- fields: {name: Soslan Djanaev, rank: 1, team: Spartak Moskva}
+ model: serializers.player
+ pk: 1
+<BLANKLINE>
+
+>>> serialized = serializers.serialize("yaml", Player.objects.all())
+>>> obj = list(serializers.deserialize("yaml", serialized))[0]
+>>> print obj
+<DeserializedObject: Soslan Djanaev (1) playing for Spartak Moskva>
+
+
"""
except ImportError:
pass