Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Fixed #11486 -- Corrected the XML serializer to allow for the seriali…

…zation of objects with a null PK value. Also includes migration of doctests to unittests (we have always been at war with doctests). Thanks to zdmytriv for the report, and Niall Kelly for the patch.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@13862 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit 11aee35a9fb72c470364a93186823a327d6840bb 1 parent 320c469
Russell Keith-Magee authored September 19, 2010
1  AUTHORS
@@ -251,6 +251,7 @@ answer newbie questions, and generally made Django that much better:
251 251
     Erik Karulf <erik@karulf.com>
252 252
     Ben Dean Kawamura <ben.dean.kawamura@gmail.com>
253 253
     Ian G. Kelly <ian.g.kelly@gmail.com>
  254
+    Niall Kelly <duke.sam.vimes@gmail.com>
254 255
     Ryan Kelly <ryan@rfk.id.au>
255 256
     Thomas Kerpe <thomas@kerpe.net>
256 257
     Wiley Kestner <wiley.kestner@gmail.com>
25  django/core/serializers/xml_serializer.py
@@ -42,10 +42,16 @@ def start_object(self, obj):
42 42
             raise base.SerializationError("Non-model object (%s) encountered during serialization" % type(obj))
43 43
 
44 44
         self.indent(1)
45  
-        self.xml.startElement("object", {
46  
-            "pk"    : smart_unicode(obj._get_pk_val()),
47  
-            "model" : smart_unicode(obj._meta),
48  
-        })
  45
+        obj_pk = obj._get_pk_val()
  46
+        if obj_pk is None:
  47
+            attrs = {"model": smart_unicode(obj._meta),}
  48
+        else:
  49
+            attrs = {
  50
+                "pk": smart_unicode(obj._get_pk_val()),
  51
+                "model": smart_unicode(obj._meta),
  52
+            }
  53
+
  54
+        self.xml.startElement("object", attrs)
49 55
 
50 56
     def end_object(self, obj):
51 57
         """
@@ -166,11 +172,12 @@ def _handle_object(self, node):
166 172
         # bail.
167 173
         Model = self._get_model_from_node(node, "model")
168 174
 
169  
-        # Start building a data dictionary from the object.  If the node is
170  
-        # missing the pk attribute, bail.
171  
-        pk = node.getAttribute("pk")
172  
-        if not pk:
173  
-            raise base.DeserializationError("<object> node is missing the 'pk' attribute")
  175
+        # Start building a data dictionary from the object.
  176
+        # If the node is missing the pk set it to None
  177
+        if node.hasAttribute("pk"):
  178
+            pk = node.getAttribute("pk")
  179
+        else:
  180
+            pk = None
174 181
 
175 182
         data = {Model._meta.pk.attname : Model._meta.pk.to_python(pk)}
176 183
 
243  tests/modeltests/serializers/models.py
@@ -18,6 +18,7 @@ class Meta:
18 18
     def __unicode__(self):
19 19
         return self.name
20 20
 
  21
+
21 22
 class Author(models.Model):
22 23
     name = models.CharField(max_length=20)
23 24
 
@@ -27,6 +28,7 @@ class Meta:
27 28
     def __unicode__(self):
28 29
         return self.name
29 30
 
  31
+
30 32
 class Article(models.Model):
31 33
     author = models.ForeignKey(Author)
32 34
     headline = models.CharField(max_length=50)
@@ -39,6 +41,7 @@ class Meta:
39 41
     def __unicode__(self):
40 42
         return self.headline
41 43
 
  44
+
42 45
 class AuthorProfile(models.Model):
43 46
     author = models.OneToOneField(Author, primary_key=True)
44 47
     date_of_birth = models.DateField()
@@ -46,6 +49,7 @@ class AuthorProfile(models.Model):
46 49
     def __unicode__(self):
47 50
         return u"Profile of %s" % self.author
48 51
 
  52
+
49 53
 class Actor(models.Model):
50 54
     name = models.CharField(max_length=20, primary_key=True)
51 55
 
@@ -55,6 +59,7 @@ class Meta:
55 59
     def __unicode__(self):
56 60
         return self.name
57 61
 
  62
+
58 63
 class Movie(models.Model):
59 64
     actor = models.ForeignKey(Actor)
60 65
     title = models.CharField(max_length=50)
@@ -66,6 +71,7 @@ class Meta:
66 71
     def __unicode__(self):
67 72
         return self.title
68 73
 
  74
+
69 75
 class Score(models.Model):
70 76
     score = models.FloatField()
71 77
 
@@ -83,6 +89,7 @@ def __str__(self):
83 89
     def to_string(self):
84 90
         return "%s" % self.title
85 91
 
  92
+
86 93
 class TeamField(models.CharField):
87 94
     __metaclass__ = models.SubfieldBase
88 95
 
@@ -100,6 +107,7 @@ def to_python(self, value):
100 107
     def value_to_string(self, obj):
101 108
         return self._get_val_from_obj(obj).to_string()
102 109
 
  110
+
103 111
 class Player(models.Model):
104 112
     name = models.CharField(max_length=50)
105 113
     rank = models.IntegerField()
@@ -107,238 +115,3 @@ class Player(models.Model):
107 115
 
108 116
     def __unicode__(self):
109 117
         return u'%s (%d) playing for %s' % (self.name, self.rank, self.team.to_string())
110  
-
111  
-__test__ = {'API_TESTS':"""
112  
-# Create some data:
113  
->>> from datetime import datetime
114  
->>> sports = Category(name="Sports")
115  
->>> music = Category(name="Music")
116  
->>> op_ed = Category(name="Op-Ed")
117  
->>> sports.save(); music.save(); op_ed.save()
118  
-
119  
->>> joe = Author(name="Joe")
120  
->>> jane = Author(name="Jane")
121  
->>> joe.save(); jane.save()
122  
-
123  
->>> a1 = Article(
124  
-...     author = jane,
125  
-...     headline = "Poker has no place on ESPN",
126  
-...     pub_date = datetime(2006, 6, 16, 11, 00))
127  
->>> a2 = Article(
128  
-...     author = joe,
129  
-...     headline = "Time to reform copyright",
130  
-...     pub_date = datetime(2006, 6, 16, 13, 00, 11, 345))
131  
->>> a1.save(); a2.save()
132  
->>> a1.categories = [sports, op_ed]
133  
->>> a2.categories = [music, op_ed]
134  
-
135  
-# Serialize a queryset to XML
136  
->>> from django.core import serializers
137  
->>> xml = serializers.serialize("xml", Article.objects.all())
138  
-
139  
-# The output is valid XML
140  
->>> from xml.dom import minidom
141  
->>> dom = minidom.parseString(xml)
142  
-
143  
-# Deserializing has a similar interface, except that special DeserializedObject
144  
-# instances are returned.  This is because data might have changed in the
145  
-# database since the data was serialized (we'll simulate that below).
146  
->>> for obj in serializers.deserialize("xml", xml):
147  
-...     print obj
148  
-<DeserializedObject: serializers.Article(pk=1)>
149  
-<DeserializedObject: serializers.Article(pk=2)>
150  
-
151  
-# Deserializing data with different field values doesn't change anything in the
152  
-# database until we call save():
153  
->>> xml = xml.replace("Poker has no place on ESPN", "Poker has no place on television")
154  
->>> objs = list(serializers.deserialize("xml", xml))
155  
-
156  
-# Even those I deserialized, the database hasn't been touched
157  
->>> Article.objects.all()
158  
-[<Article: Poker has no place on ESPN>, <Article: Time to reform copyright>]
159  
-
160  
-# But when I save, the data changes as you might except.
161  
->>> objs[0].save()
162  
->>> Article.objects.all()
163  
-[<Article: Poker has no place on television>, <Article: Time to reform copyright>]
164  
-
165  
-# Django also ships with a built-in JSON serializers
166  
->>> json = serializers.serialize("json", Category.objects.filter(pk=2))
167  
->>> json
168  
-'[{"pk": 2, "model": "serializers.category", "fields": {"name": "Music"}}]'
169  
-
170  
-# You can easily create new objects by deserializing data with an empty PK
171  
-# (It's easier to demo this with JSON...)
172  
->>> new_author_json = '[{"pk": null, "model": "serializers.author", "fields": {"name": "Bill"}}]'
173  
->>> for obj in serializers.deserialize("json", new_author_json):
174  
-...     obj.save()
175  
->>> Author.objects.all()
176  
-[<Author: Bill>, <Author: Jane>, <Author: Joe>]
177  
-
178  
-# All the serializers work the same
179  
->>> json = serializers.serialize("json", Article.objects.all())
180  
->>> for obj in serializers.deserialize("json", json):
181  
-...     print obj
182  
-<DeserializedObject: serializers.Article(pk=1)>
183  
-<DeserializedObject: serializers.Article(pk=2)>
184  
-
185  
->>> json = json.replace("Poker has no place on television", "Just kidding; I love TV poker")
186  
->>> for obj in serializers.deserialize("json", json):
187  
-...     obj.save()
188  
-
189  
->>> Article.objects.all()
190  
-[<Article: Just kidding; I love TV poker>, <Article: Time to reform copyright>]
191  
-
192  
-# If you use your own primary key field (such as a OneToOneField),
193  
-# it doesn't appear in the serialized field list - it replaces the
194  
-# pk identifier.
195  
->>> profile = AuthorProfile(author=joe, date_of_birth=datetime(1970,1,1))
196  
->>> profile.save()
197  
-
198  
->>> json = serializers.serialize("json", AuthorProfile.objects.all())
199  
->>> json
200  
-'[{"pk": 1, "model": "serializers.authorprofile", "fields": {"date_of_birth": "1970-01-01"}}]'
201  
-
202  
->>> for obj in serializers.deserialize("json", json):
203  
-...     print obj
204  
-<DeserializedObject: serializers.AuthorProfile(pk=1)>
205  
-
206  
-# Objects ids can be referenced before they are defined in the serialization data
207  
-# However, the deserialization process will need to be contained within a transaction
208  
->>> json = '[{"pk": 3, "model": "serializers.article", "fields": {"headline": "Forward references pose no problem", "pub_date": "2006-06-16 15:00:00", "categories": [4, 1], "author": 4}}, {"pk": 4, "model": "serializers.category", "fields": {"name": "Reference"}}, {"pk": 4, "model": "serializers.author", "fields": {"name": "Agnes"}}]'
209  
->>> from django.db import transaction
210  
->>> transaction.enter_transaction_management()
211  
->>> transaction.managed(True)
212  
->>> for obj in serializers.deserialize("json", json):
213  
-...     obj.save()
214  
-
215  
->>> transaction.commit()
216  
->>> transaction.leave_transaction_management()
217  
-
218  
->>> article = Article.objects.get(pk=3)
219  
->>> article
220  
-<Article: Forward references pose no problem>
221  
->>> article.categories.all()
222  
-[<Category: Reference>, <Category: Sports>]
223  
->>> article.author
224  
-<Author: Agnes>
225  
-
226  
-# Serializer output can be restricted to a subset of fields
227  
->>> print serializers.serialize("json", Article.objects.all(), fields=('headline','pub_date'))
228  
-[{"pk": 1, "model": "serializers.article", "fields": {"headline": "Just kidding; I love TV poker", "pub_date": "2006-06-16 11:00:00"}}, {"pk": 2, "model": "serializers.article", "fields": {"headline": "Time to reform copyright", "pub_date": "2006-06-16 13:00:11"}}, {"pk": 3, "model": "serializers.article", "fields": {"headline": "Forward references pose no problem", "pub_date": "2006-06-16 15:00:00"}}]
229  
-
230  
-# Every string is serialized as a unicode object, also primary key
231  
-# which is 'varchar'
232  
->>> ac = Actor(name="Zażółć")
233  
->>> mv = Movie(title="Gęślą jaźń", actor=ac)
234  
->>> ac.save(); mv.save()
235  
-
236  
-# Let's serialize our movie
237  
->>> print serializers.serialize("json", [mv])
238  
-[{"pk": 1, "model": "serializers.movie", "fields": {"price": "0.00", "actor": "Za\u017c\u00f3\u0142\u0107", "title": "G\u0119\u015bl\u0105 ja\u017a\u0144"}}]
239  
-
240  
-# Deserialization of movie
241  
->>> list(serializers.deserialize('json', serializers.serialize('json', [mv])))[0].object.title
242  
-u'G\u0119\u015bl\u0105 ja\u017a\u0144'
243  
-
244  
-# None is null after serialization to json
245  
-# Primary key is None in case of not saved model
246  
->>> mv2 = Movie(title="Movie 2", actor=ac)
247  
->>> print serializers.serialize("json", [mv2])
248  
-[{"pk": null, "model": "serializers.movie", "fields": {"price": "0.00", "actor": "Za\u017c\u00f3\u0142\u0107", "title": "Movie 2"}}]
249  
-
250  
-# Deserialization of null returns None for pk
251  
->>> print list(serializers.deserialize('json', serializers.serialize('json', [mv2])))[0].object.id
252  
-None
253  
-
254  
-# Serialization and deserialization of floats:
255  
->>> sc = Score(score=3.4)
256  
->>> print serializers.serialize("json", [sc])
257  
-[{"pk": null, "model": "serializers.score", "fields": {"score": 3.4}}]
258  
->>> print list(serializers.deserialize('json', serializers.serialize('json', [sc])))[0].object.score
259  
-3.4
260  
-
261  
-# Custom field with non trivial to string convertion value
262  
->>> player = Player()
263  
->>> player.name = "Soslan Djanaev"
264  
->>> player.rank = 1
265  
->>> player.team = Team("Spartak Moskva")
266  
->>> player.save()
267  
-
268  
->>> serialized = serializers.serialize("json", Player.objects.all())
269  
->>> print serialized
270  
-[{"pk": 1, "model": "serializers.player", "fields": {"name": "Soslan Djanaev", "rank": 1, "team": "Spartak Moskva"}}]
271  
-
272  
->>> obj = list(serializers.deserialize("json", serialized))[0]
273  
->>> print obj
274  
-<DeserializedObject: serializers.Player(pk=1)>
275  
-
276  
-# Regression for #12524 -- dates before 1000AD get prefixed 0's on the year
277  
->>> a = Article.objects.create(
278  
-...     pk=4,
279  
-...     author = jane,
280  
-...     headline = "Nobody remembers the early years",
281  
-...     pub_date = datetime(1, 2, 3, 4, 5, 6))
282  
-
283  
->>> serialized = serializers.serialize("json", [a])
284  
->>> print serialized
285  
-[{"pk": 4, "model": "serializers.article", "fields": {"headline": "Nobody remembers the early years", "pub_date": "0001-02-03 04:05:06", "categories": [], "author": 2}}]
286  
-
287  
->>> obj = list(serializers.deserialize("json", serialized))[0]
288  
->>> print obj.object.pub_date
289  
-0001-02-03 04:05:06
290  
-
291  
-"""}
292  
-
293  
-try:
294  
-    import yaml
295  
-    __test__['YAML'] = """
296  
-# Create some data:
297  
-
298  
->>> articles = Article.objects.all().order_by("id")[:2]
299  
->>> from django.core import serializers
300  
-
301  
-# test if serial
302  
-
303  
->>> serialized = serializers.serialize("yaml", articles)
304  
->>> print serialized
305  
-- fields:
306  
-    author: 2
307  
-    categories: [3, 1]
308  
-    headline: Just kidding; I love TV poker
309  
-    pub_date: 2006-06-16 11:00:00
310  
-  model: serializers.article
311  
-  pk: 1
312  
-- fields:
313  
-    author: 1
314  
-    categories: [2, 3]
315  
-    headline: Time to reform copyright
316  
-    pub_date: 2006-06-16 13:00:11
317  
-  model: serializers.article
318  
-  pk: 2
319  
-<BLANKLINE>
320  
-
321  
->>> obs = list(serializers.deserialize("yaml", serialized))
322  
->>> for i in obs:
323  
-...     print i
324  
-<DeserializedObject: serializers.Article(pk=1)>
325  
-<DeserializedObject: serializers.Article(pk=2)>
326  
-
327  
-# Custom field with non trivial to string convertion value with YAML serializer
328  
-
329  
->>> print serializers.serialize("yaml", Player.objects.all())
330  
-- fields: {name: Soslan Djanaev, rank: 1, team: Spartak Moskva}
331  
-  model: serializers.player
332  
-  pk: 1
333  
-<BLANKLINE>
334  
-
335  
->>> serialized = serializers.serialize("yaml", Player.objects.all())
336  
->>> obj = list(serializers.deserialize("yaml", serialized))[0]
337  
->>> print obj
338  
-<DeserializedObject: serializers.Player(pk=1)>
339  
-
340  
-
341  
-"""
342  
-except ImportError:
343  
-    pass
344  
-
417  tests/modeltests/serializers/tests.py
... ...
@@ -0,0 +1,417 @@
  1
+# -*- coding: utf-8 -*-
  2
+from datetime import datetime
  3
+from StringIO import StringIO
  4
+from xml.dom import minidom
  5
+
  6
+from django.core import serializers
  7
+from django.db import transaction
  8
+from django.test import TestCase, TransactionTestCase, Approximate
  9
+from django.utils import simplejson
  10
+
  11
+from models import Category, Author, Article, AuthorProfile, Actor, \
  12
+                                    Movie, Score, Player, Team
  13
+
  14
+class SerializersTestBase(object):
  15
+    @staticmethod
  16
+    def _comparison_value(value):
  17
+        return value
  18
+
  19
+    def setUp(self):
  20
+        sports = Category.objects.create(name="Sports")
  21
+        music = Category.objects.create(name="Music")
  22
+        op_ed = Category.objects.create(name="Op-Ed")
  23
+
  24
+        self.joe = Author.objects.create(name="Joe")
  25
+        self.jane = Author.objects.create(name="Jane")
  26
+
  27
+        self.a1 = Article(
  28
+            author=self.jane,
  29
+            headline="Poker has no place on ESPN",
  30
+            pub_date=datetime(2006, 6, 16, 11, 00)
  31
+        )
  32
+        self.a1.save()
  33
+        self.a1.categories = [sports, op_ed]
  34
+
  35
+        self.a2 = Article(
  36
+            author=self.joe,
  37
+            headline="Time to reform copyright",
  38
+            pub_date=datetime(2006, 6, 16, 13, 00, 11, 345)
  39
+        )
  40
+        self.a2.save()
  41
+        self.a2.categories = [music, op_ed]
  42
+
  43
+    def test_serialize(self):
  44
+        """Tests that basic serialization works."""
  45
+        serial_str = serializers.serialize(self.serializer_name,
  46
+                                           Article.objects.all())
  47
+        self.assertTrue(self._validate_output(serial_str))
  48
+
  49
+    def test_serializer_roundtrip(self):
  50
+        """Tests that serialized content can be deserialized."""
  51
+        serial_str = serializers.serialize(self.serializer_name,
  52
+                                           Article.objects.all())
  53
+        models = list(serializers.deserialize(self.serializer_name, serial_str))
  54
+        self.assertEqual(len(models), 2)
  55
+
  56
+    def test_altering_serialized_output(self):
  57
+        """
  58
+        Tests the ability to create new objects by
  59
+        modifying serialized content.
  60
+        """
  61
+        old_headline = "Poker has no place on ESPN"
  62
+        new_headline = "Poker has no place on television"
  63
+        serial_str = serializers.serialize(self.serializer_name,
  64
+                                           Article.objects.all())
  65
+        serial_str = serial_str.replace(old_headline, new_headline)
  66
+        models = list(serializers.deserialize(self.serializer_name, serial_str))
  67
+
  68
+        # Prior to saving, old headline is in place
  69
+        self.assertTrue(Article.objects.filter(headline=old_headline))
  70
+        self.assertFalse(Article.objects.filter(headline=new_headline))
  71
+
  72
+        for model in models:
  73
+            model.save()
  74
+
  75
+        # After saving, new headline is in place
  76
+        self.assertTrue(Article.objects.filter(headline=new_headline))
  77
+        self.assertFalse(Article.objects.filter(headline=old_headline))
  78
+
  79
+    def test_one_to_one_as_pk(self):
  80
+        """
  81
+        Tests that if you use your own primary key field
  82
+        (such as a OneToOneField), it doesn't appear in the
  83
+        serialized field list - it replaces the pk identifier.
  84
+        """
  85
+        profile = AuthorProfile(author=self.joe,
  86
+                                date_of_birth=datetime(1970,1,1))
  87
+        profile.save()
  88
+        serial_str = serializers.serialize(self.serializer_name,
  89
+                                           AuthorProfile.objects.all())
  90
+        self.assertFalse(self._get_field_values(serial_str, 'author'))
  91
+
  92
+        for obj in serializers.deserialize(self.serializer_name, serial_str):
  93
+            self.assertEqual(obj.object.pk, self._comparison_value(self.joe.pk))
  94
+
  95
+    def test_serialize_field_subset(self):
  96
+        """Tests that output can be restricted to a subset of fields"""
  97
+        valid_fields = ('headline','pub_date')
  98
+        invalid_fields = ("author", "categories")
  99
+        serial_str = serializers.serialize(self.serializer_name,
  100
+                                    Article.objects.all(),
  101
+                                    fields=valid_fields)
  102
+        for field_name in invalid_fields:
  103
+            self.assertFalse(self._get_field_values(serial_str, field_name))
  104
+
  105
+        for field_name in valid_fields:
  106
+            self.assertTrue(self._get_field_values(serial_str, field_name))
  107
+
  108
+    def test_serialize_unicode(self):
  109
+        """Tests that unicode makes the roundtrip intact"""
  110
+        actor_name = u"Za\u017c\u00f3\u0142\u0107"
  111
+        movie_title = u'G\u0119\u015bl\u0105 ja\u017a\u0144'
  112
+        ac = Actor(name=actor_name)
  113
+        mv = Movie(title=movie_title, actor=ac)
  114
+        ac.save()
  115
+        mv.save()
  116
+
  117
+        serial_str = serializers.serialize(self.serializer_name, [mv])
  118
+        self.assertEqual(self._get_field_values(serial_str, "title")[0], movie_title)
  119
+        self.assertEqual(self._get_field_values(serial_str, "actor")[0], actor_name)
  120
+
  121
+        obj_list = list(serializers.deserialize(self.serializer_name, serial_str))
  122
+        mv_obj = obj_list[0].object
  123
+        self.assertEqual(mv_obj.title, movie_title)
  124
+
  125
+    def test_serialize_with_null_pk(self):
  126
+        """
  127
+        Tests that serialized data with no primary key results
  128
+        in a model instance with no id
  129
+        """
  130
+        category = Category(name="Reference")
  131
+        serial_str = serializers.serialize(self.serializer_name, [category])
  132
+        pk_value = self._get_pk_values(serial_str)[0]
  133
+        self.assertFalse(pk_value)
  134
+
  135
+        cat_obj = list(serializers.deserialize(self.serializer_name,
  136
+                                               serial_str))[0].object
  137
+        self.assertEqual(cat_obj.id, None)
  138
+
  139
+    def test_float_serialization(self):
  140
+        """Tests that float values serialize and deserialize intact"""
  141
+        sc = Score(score=3.4)
  142
+        sc.save()
  143
+        serial_str = serializers.serialize(self.serializer_name, [sc])
  144
+        deserial_objs = list(serializers.deserialize(self.serializer_name,
  145
+                                                serial_str))
  146
+        self.assertEqual(deserial_objs[0].object.score, Approximate(3.4, places=1))
  147
+
  148
+    def test_custom_field_serialization(self):
  149
+        """Tests that custom fields serialize and deserialize intact"""
  150
+        team_str = "Spartak Moskva"
  151
+        player = Player()
  152
+        player.name = "Soslan Djanaev"
  153
+        player.rank = 1
  154
+        player.team = Team(team_str)
  155
+        player.save()
  156
+        serial_str = serializers.serialize(self.serializer_name,
  157
+                                           Player.objects.all())
  158
+        team = self._get_field_values(serial_str, "team")
  159
+        self.assertTrue(team)
  160
+        self.assertEqual(team[0], team_str)
  161
+
  162
+        deserial_objs = list(serializers.deserialize(self.serializer_name, serial_str))
  163
+        self.assertEqual(deserial_objs[0].object.team.to_string(),
  164
+                         player.team.to_string())
  165
+
  166
+    def test_pre_1000ad_date(self):
  167
+        """Tests that year values before 1000AD are properly formatted"""
  168
+        # Regression for #12524 -- dates before 1000AD get prefixed
  169
+        # 0's on the year
  170
+        a = Article.objects.create(
  171
+        author = self.jane,
  172
+        headline = "Nobody remembers the early years",
  173
+        pub_date = datetime(1, 2, 3, 4, 5, 6))
  174
+
  175
+        serial_str = serializers.serialize(self.serializer_name, [a])
  176
+        date_values = self._get_field_values(serial_str, "pub_date")
  177
+        self.assertEquals(date_values[0], "0001-02-03 04:05:06")
  178
+
  179
+    def test_pkless_serialized_strings(self):
  180
+        """
  181
+        Tests that serialized strings without PKs
  182
+        can be turned into models
  183
+        """
  184
+        deserial_objs = list(serializers.deserialize(self.serializer_name,
  185
+                                                     self.pkless_str))
  186
+        for obj in deserial_objs:
  187
+            self.assertFalse(obj.object.id)
  188
+            obj.save()
  189
+        self.assertEqual(Category.objects.all().count(), 4)
  190
+
  191
+
  192
+class SerializersTransactionTestBase(object):
  193
+    def test_forward_refs(self):
  194
+        """
  195
+        Tests that objects ids can be referenced before they are
  196
+        defined in the serialization data.
  197
+        """
  198
+        # The deserialization process needs to be contained
  199
+        # within a transaction in order to test forward reference
  200
+        # handling.
  201
+        transaction.enter_transaction_management()
  202
+        transaction.managed(True)
  203
+        objs = serializers.deserialize(self.serializer_name, self.fwd_ref_str)
  204
+        for obj in objs:
  205
+            obj.save()
  206
+        transaction.commit()
  207
+        transaction.leave_transaction_management()
  208
+
  209
+        for model_cls in (Category, Author, Article):
  210
+            self.assertEqual(model_cls.objects.all().count(), 1)
  211
+        art_obj = Article.objects.all()[0]
  212
+        self.assertEqual(art_obj.categories.all().count(), 1)
  213
+        self.assertEqual(art_obj.author.name, "Agnes")
  214
+
  215
+
  216
+class XmlSerializerTestCase(SerializersTestBase, TestCase):
  217
+    serializer_name = "xml"
  218
+    pkless_str = """<?xml version="1.0" encoding="utf-8"?>
  219
+<django-objects version="1.0">
  220
+    <object model="serializers.category">
  221
+        <field type="CharField" name="name">Reference</field>
  222
+    </object>
  223
+</django-objects>"""
  224
+
  225
+    @staticmethod
  226
+    def _comparison_value(value):
  227
+        # The XML serializer handles everything as strings, so comparisons
  228
+        # need to be performed on the stringified value
  229
+        return unicode(value)
  230
+
  231
+    @staticmethod
  232
+    def _validate_output(serial_str):
  233
+        try:
  234
+            minidom.parseString(serial_str)
  235
+        except Exception:
  236
+            return False
  237
+        else:
  238
+            return True
  239
+
  240
+    @staticmethod
  241
+    def _get_pk_values(serial_str):
  242
+        ret_list = []
  243
+        dom = minidom.parseString(serial_str)
  244
+        fields = dom.getElementsByTagName("object")
  245
+        for field in fields:
  246
+            ret_list.append(field.getAttribute("pk"))
  247
+        return ret_list
  248
+
  249
+    @staticmethod
  250
+    def _get_field_values(serial_str, field_name):
  251
+        ret_list = []
  252
+        dom = minidom.parseString(serial_str)
  253
+        fields = dom.getElementsByTagName("field")
  254
+        for field in fields:
  255
+            if field.getAttribute("name") == field_name:
  256
+                temp = []
  257
+                for child in field.childNodes:
  258
+                    temp.append(child.nodeValue)
  259
+                ret_list.append("".join(temp))
  260
+        return ret_list
  261
+
  262
+class XmlSerializerTransactionTestCase(SerializersTransactionTestBase, TransactionTestCase):
  263
+    serializer_name = "xml"
  264
+    fwd_ref_str = """<?xml version="1.0" encoding="utf-8"?>
  265
+<django-objects version="1.0">
  266
+    <object pk="1" model="serializers.article">
  267
+        <field to="serializers.author" name="author" rel="ManyToOneRel">1</field>
  268
+        <field type="CharField" name="headline">Forward references pose no problem</field>
  269
+        <field type="DateTimeField" name="pub_date">2006-06-16 15:00:00</field>
  270
+        <field to="serializers.category" name="categories" rel="ManyToManyRel">
  271
+            <object pk="1"></object>
  272
+        </field>
  273
+    </object>
  274
+    <object pk="1" model="serializers.author">
  275
+        <field type="CharField" name="name">Agnes</field>
  276
+    </object>
  277
+    <object pk="1" model="serializers.category">
  278
+        <field type="CharField" name="name">Reference</field></object>
  279
+</django-objects>"""
  280
+
  281
+
  282
+class JsonSerializerTestCase(SerializersTestBase, TestCase):
  283
+    serializer_name = "json"
  284
+    pkless_str = """[{"pk": null, "model": "serializers.category", "fields": {"name": "Reference"}}]"""
  285
+
  286
+    @staticmethod
  287
+    def _validate_output(serial_str):
  288
+        try:
  289
+            simplejson.loads(serial_str)
  290
+        except Exception:
  291
+            return False
  292
+        else:
  293
+            return True
  294
+
  295
+    @staticmethod
  296
+    def _get_pk_values(serial_str):
  297
+        ret_list = []
  298
+        serial_list = simplejson.loads(serial_str)
  299
+        for obj_dict in serial_list:
  300
+            ret_list.append(obj_dict["pk"])
  301
+        return ret_list
  302
+
  303
+    @staticmethod
  304
+    def _get_field_values(serial_str, field_name):
  305
+        ret_list = []
  306
+        serial_list = simplejson.loads(serial_str)
  307
+        for obj_dict in serial_list:
  308
+            if field_name in obj_dict["fields"]:
  309
+                ret_list.append(obj_dict["fields"][field_name])
  310
+        return ret_list
  311
+
  312
+class JsonSerializerTransactionTestCase(SerializersTransactionTestBase, TransactionTestCase):
  313
+    serializer_name = "json"
  314
+    fwd_ref_str = """[
  315
+    {
  316
+        "pk": 1,
  317
+        "model": "serializers.article",
  318
+        "fields": {
  319
+            "headline": "Forward references pose no problem",
  320
+            "pub_date": "2006-06-16 15:00:00",
  321
+            "categories": [1],
  322
+            "author": 1
  323
+        }
  324
+    },
  325
+    {
  326
+        "pk": 1,
  327
+        "model": "serializers.category",
  328
+        "fields": {
  329
+            "name": "Reference"
  330
+        }
  331
+    },
  332
+    {
  333
+        "pk": 1,
  334
+        "model": "serializers.author",
  335
+        "fields": {
  336
+            "name": "Agnes"
  337
+        }
  338
+    }]"""
  339
+
  340
+try:
  341
+    import yaml
  342
+except ImportError:
  343
+    pass
  344
+else:
  345
+    class YamlSerializerTestCase(SerializersTestBase, TestCase):
  346
+        serializer_name = "yaml"
  347
+        fwd_ref_str = """- fields:
  348
+    headline: Forward references pose no problem
  349
+    pub_date: 2006-06-16 15:00:00
  350
+    categories: [1]
  351
+    author: 1
  352
+  pk: 1
  353
+  model: serializers.article
  354
+- fields:
  355
+    name: Reference
  356
+  pk: 1
  357
+  model: serializers.category
  358
+- fields:
  359
+    name: Agnes
  360
+  pk: 1
  361
+  model: serializers.author"""
  362
+
  363
+        pkless_str = """- fields:
  364
+    name: Reference
  365
+  pk: null
  366
+  model: serializers.category"""
  367
+
  368
+        @staticmethod
  369
+        def _validate_output(serial_str):
  370
+            try:
  371
+                yaml.load(StringIO(serial_str))
  372
+            except Exception:
  373
+                return False
  374
+            else:
  375
+                return True
  376
+
  377
+        @staticmethod
  378
+        def _get_pk_values(serial_str):
  379
+            ret_list = []
  380
+            stream = StringIO(serial_str)
  381
+            for obj_dict in yaml.load(stream):
  382
+                ret_list.append(obj_dict["pk"])
  383
+            return ret_list
  384
+
  385
+        @staticmethod
  386
+        def _get_field_values(serial_str, field_name):
  387
+            ret_list = []
  388
+            stream = StringIO(serial_str)
  389
+            for obj_dict in yaml.load(stream):
  390
+                if "fields" in obj_dict and field_name in obj_dict["fields"]:
  391
+                    field_value = obj_dict["fields"][field_name]
  392
+                    # yaml.load will return non-string objects for some
  393
+                    # of the fields we are interested in, this ensures that
  394
+                    # everything comes back as a string
  395
+                    if isinstance(field_value, basestring):
  396
+                        ret_list.append(field_value)
  397
+                    else:
  398
+                        ret_list.append(str(field_value))
  399
+            return ret_list
  400
+
  401
+    class YamlSerializerTransactionTestCase(SerializersTransactionTestBase, TransactionTestCase):
  402
+        serializer_name = "yaml"
  403
+        fwd_ref_str = """- fields:
  404
+    headline: Forward references pose no problem
  405
+    pub_date: 2006-06-16 15:00:00
  406
+    categories: [1]
  407
+    author: 1
  408
+  pk: 1
  409
+  model: serializers.article
  410
+- fields:
  411
+    name: Reference
  412
+  pk: 1
  413
+  model: serializers.category
  414
+- fields:
  415
+    name: Agnes
  416
+  pk: 1
  417
+  model: serializers.author"""

0 notes on commit 11aee35

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