Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

[1.0.X] 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.

Merge of r10554 from trunk.


git-svn-id: http://code.djangoproject.com/svn/django/branches/releases/1.0.X@10555 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit 960d3172f630b80c909ffb517356fdb3161d2d3a 1 parent 6be2d90
Russell Keith-Magee authored April 13, 2009
15  django/core/serializers/python.py
@@ -7,15 +7,15 @@
7 7
 from django.conf import settings
8 8
 from django.core.serializers import base
9 9
 from django.db import models
10  
-from django.utils.encoding import smart_unicode
  10
+from django.utils.encoding import smart_unicode, is_protected_type
11 11
 
12 12
 class Serializer(base.Serializer):
13 13
     """
14 14
     Serializes a QuerySet to basic Python objects.
15 15
     """
16  
-    
  16
+
17 17
     internal_use_only = True
18  
-    
  18
+
19 19
     def start_serialization(self):
20 20
         self._current = None
21 21
         self.objects = []
@@ -35,7 +35,14 @@ def end_object(self, obj):
35 35
         self._current = None
36 36
 
37 37
     def handle_field(self, obj, field):
38  
-        self._current[field.name] = smart_unicode(getattr(obj, field.name), strings_only=True)
  38
+        value = field._get_val_from_obj(obj)
  39
+        # Protected types (i.e., primitives like None, numbers, dates,
  40
+        # and Decimals) are passed through as is. All other values are
  41
+        # converted to string first.
  42
+        if is_protected_type(value):
  43
+            self._current[field.name] = value
  44
+        else:
  45
+            self._current[field.name] = field.value_to_string(obj)
39 46
 
40 47
     def handle_fk_field(self, obj, field):
41 48
         related = getattr(obj, field.name)
6  django/core/serializers/xml_serializer.py
@@ -65,11 +65,9 @@ def handle_field(self, obj, field):
65 65
             "type" : field.get_internal_type()
66 66
         })
67 67
 
68  
-        # Get a "string version" of the object's data (this is handled by the
69  
-        # serializer base class).
  68
+        # Get a "string version" of the object's data.
70 69
         if getattr(obj, field.name) is not None:
71  
-            value = self.get_string_value(obj, field)
72  
-            self.xml.characters(smart_unicode(value))
  70
+            self.xml.characters(field.value_to_string(obj))
73 71
         else:
74 72
             self.xml.addQuickElement("None")
75 73
 
15  django/utils/encoding.py
@@ -41,6 +41,19 @@ def smart_unicode(s, encoding='utf-8', strings_only=False, errors='strict'):
41 41
         return s
42 42
     return force_unicode(s, encoding, strings_only, errors)
43 43
 
  44
+def is_protected_type(obj):
  45
+    """Determine if the object instance is of a protected type.
  46
+
  47
+    Objects of protected types are preserved as-is when passed to
  48
+    force_unicode(strings_only=True).
  49
+    """
  50
+    return isinstance(obj, (
  51
+        types.NoneType,
  52
+        int, long,
  53
+        datetime.datetime, datetime.date, datetime.time,
  54
+        float, Decimal)
  55
+    )
  56
+
44 57
 def force_unicode(s, encoding='utf-8', strings_only=False, errors='strict'):
45 58
     """
46 59
     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'):
48 61
 
49 62
     If strings_only is True, don't convert (some) non-string-like objects.
50 63
     """
51  
-    if strings_only and isinstance(s, (types.NoneType, int, long, datetime.datetime, datetime.date, datetime.time, float, Decimal)):
  64
+    if strings_only and is_protected_type(s):
52 65
         return s
53 66
     try:
54 67
         if not isinstance(s, basestring,):
68  tests/modeltests/serializers/models.py
@@ -73,6 +73,45 @@ def __unicode__(self):
73 73
 class Score(models.Model):
74 74
     score = models.FloatField()
75 75
 
  76
+
  77
+class Team(object):
  78
+    def __init__(self, title):
  79
+        self.title = title
  80
+
  81
+    def __unicode__(self):
  82
+        raise NotImplementedError("Not so simple")
  83
+
  84
+    def __str__(self):
  85
+        raise NotImplementedError("Not so simple")
  86
+
  87
+    def to_string(self):
  88
+        return "%s" % self.title
  89
+
  90
+class TeamField(models.CharField):
  91
+    __metaclass__ = models.SubfieldBase
  92
+
  93
+    def __init__(self):
  94
+        super(TeamField, self).__init__(max_length=100)
  95
+
  96
+    def get_db_prep_save(self, value):
  97
+        return unicode(value.title)
  98
+
  99
+    def to_python(self, value):
  100
+        if isinstance(value, Team):
  101
+            return value
  102
+        return Team(value)
  103
+
  104
+    def value_to_string(self, obj):
  105
+        return self._get_val_from_obj(obj).to_string()
  106
+
  107
+class Player(models.Model):
  108
+    name = models.CharField(max_length=50)
  109
+    rank = models.IntegerField()
  110
+    team = TeamField()
  111
+
  112
+    def __unicode__(self):
  113
+        return u'%s (%d) playing for %s' % (self.name, self.rank, self.team.to_string())
  114
+
76 115
 __test__ = {'API_TESTS':"""
77 116
 # Create some data:
78 117
 >>> from datetime import datetime
@@ -223,6 +262,21 @@ class Score(models.Model):
223 262
 >>> print list(serializers.deserialize('json', serializers.serialize('json', [sc])))[0].object.score
224 263
 3.4
225 264
 
  265
+# Custom field with non trivial to string convertion value
  266
+>>> player = Player()
  267
+>>> player.name = "Soslan Djanaev"
  268
+>>> player.rank = 1
  269
+>>> player.team = Team("Spartak Moskva")
  270
+>>> player.save()
  271
+
  272
+>>> serialized = serializers.serialize("json", Player.objects.all())
  273
+>>> print serialized
  274
+[{"pk": 1, "model": "serializers.player", "fields": {"name": "Soslan Djanaev", "rank": 1, "team": "Spartak Moskva"}}]
  275
+
  276
+>>> obj = list(serializers.deserialize("json", serialized))[0]
  277
+>>> print obj
  278
+<DeserializedObject: Soslan Djanaev (1) playing for Spartak Moskva>
  279
+
226 280
 """}
227 281
 
228 282
 try:
@@ -259,6 +313,20 @@ class Score(models.Model):
259 313
 <DeserializedObject: Just kidding; I love TV poker>
260 314
 <DeserializedObject: Time to reform copyright>
261 315
 
  316
+# Custom field with non trivial to string convertion value with YAML serializer
  317
+
  318
+>>> print serializers.serialize("yaml", Player.objects.all())
  319
+- fields: {name: Soslan Djanaev, rank: 1, team: Spartak Moskva}
  320
+  model: serializers.player
  321
+  pk: 1
  322
+<BLANKLINE>
  323
+
  324
+>>> serialized = serializers.serialize("yaml", Player.objects.all())
  325
+>>> obj = list(serializers.deserialize("yaml", serialized))[0]
  326
+>>> print obj
  327
+<DeserializedObject: Soslan Djanaev (1) playing for Spartak Moskva>
  328
+
  329
+
262 330
 """
263 331
 except ImportError:
264 332
     pass

0 notes on commit 960d317

Please sign in to comment.
Something went wrong with that request. Please try again.