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
This commit is contained in:
parent
fd3ee7d786
commit
cb43898d49
|
@ -7,7 +7,7 @@ other serializers.
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core.serializers import base
|
from django.core.serializers import base
|
||||||
from django.db import models
|
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):
|
class Serializer(base.Serializer):
|
||||||
"""
|
"""
|
||||||
|
@ -35,7 +35,14 @@ class Serializer(base.Serializer):
|
||||||
self._current = None
|
self._current = None
|
||||||
|
|
||||||
def handle_field(self, obj, field):
|
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):
|
def handle_fk_field(self, obj, field):
|
||||||
related = getattr(obj, field.name)
|
related = getattr(obj, field.name)
|
||||||
|
|
|
@ -65,11 +65,9 @@ class Serializer(base.Serializer):
|
||||||
"type" : field.get_internal_type()
|
"type" : field.get_internal_type()
|
||||||
})
|
})
|
||||||
|
|
||||||
# Get a "string version" of the object's data (this is handled by the
|
# Get a "string version" of the object's data.
|
||||||
# serializer base class).
|
|
||||||
if getattr(obj, field.name) is not None:
|
if getattr(obj, field.name) is not None:
|
||||||
value = self.get_string_value(obj, field)
|
self.xml.characters(field.value_to_string(obj))
|
||||||
self.xml.characters(smart_unicode(value))
|
|
||||||
else:
|
else:
|
||||||
self.xml.addQuickElement("None")
|
self.xml.addQuickElement("None")
|
||||||
|
|
||||||
|
|
|
@ -41,6 +41,19 @@ def smart_unicode(s, encoding='utf-8', strings_only=False, errors='strict'):
|
||||||
return s
|
return s
|
||||||
return force_unicode(s, encoding, strings_only, errors)
|
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'):
|
def force_unicode(s, encoding='utf-8', strings_only=False, errors='strict'):
|
||||||
"""
|
"""
|
||||||
Similar to smart_unicode, except that lazy instances are resolved to
|
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 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
|
return s
|
||||||
try:
|
try:
|
||||||
if not isinstance(s, basestring,):
|
if not isinstance(s, basestring,):
|
||||||
|
|
|
@ -73,6 +73,45 @@ class Movie(models.Model):
|
||||||
class Score(models.Model):
|
class Score(models.Model):
|
||||||
score = models.FloatField()
|
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':"""
|
__test__ = {'API_TESTS':"""
|
||||||
# Create some data:
|
# Create some data:
|
||||||
>>> from datetime import datetime
|
>>> from datetime import datetime
|
||||||
|
@ -223,6 +262,21 @@ None
|
||||||
>>> print list(serializers.deserialize('json', serializers.serialize('json', [sc])))[0].object.score
|
>>> print list(serializers.deserialize('json', serializers.serialize('json', [sc])))[0].object.score
|
||||||
3.4
|
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:
|
try:
|
||||||
|
@ -259,6 +313,20 @@ try:
|
||||||
<DeserializedObject: Just kidding; I love TV poker>
|
<DeserializedObject: Just kidding; I love TV poker>
|
||||||
<DeserializedObject: Time to reform copyright>
|
<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:
|
except ImportError:
|
||||||
pass
|
pass
|
||||||
|
|
Loading…
Reference in New Issue