/tests/modeltests/serializers/tests.py
Python | 468 lines | 407 code | 33 blank | 28 comment | 10 complexity | 8d60417ff1bae4d559da1c05d1df91ff MD5 | raw file
1# -*- coding: utf-8 -*- 2from datetime import datetime 3from StringIO import StringIO 4from xml.dom import minidom 5 6from django.conf import settings 7from django.core import serializers 8from django.db import transaction 9from django.test import TestCase, TransactionTestCase, Approximate 10from django.utils import simplejson, unittest 11 12from models import Category, Author, Article, AuthorProfile, Actor, \ 13 Movie, Score, Player, Team 14 15class SerializerRegistrationTests(unittest.TestCase): 16 def setUp(self): 17 self.old_SERIALIZATION_MODULES = getattr(settings, 'SERIALIZATION_MODULES', None) 18 self.old_serializers = serializers._serializers 19 20 serializers._serializers = {} 21 settings.SERIALIZATION_MODULES = { 22 "json2" : "django.core.serializers.json", 23 } 24 25 def tearDown(self): 26 serializers._serializers = self.old_serializers 27 if self.old_SERIALIZATION_MODULES: 28 settings.SERIALIZATION_MODULES = self.old_SERIALIZATION_MODULES 29 else: 30 delattr(settings, 'SERIALIZATION_MODULES') 31 32 def test_register(self): 33 "Registering a new serializer populates the full registry. Refs #14823" 34 serializers.register_serializer('json3', 'django.core.serializers.json') 35 36 public_formats = serializers.get_public_serializer_formats() 37 self.assertIn('json3', public_formats) 38 self.assertIn('json2', public_formats) 39 self.assertIn('xml', public_formats) 40 41 def test_unregister(self): 42 "Unregistering a serializer doesn't cause the registry to be repopulated. Refs #14823" 43 serializers.unregister_serializer('xml') 44 serializers.register_serializer('json3', 'django.core.serializers.json') 45 46 public_formats = serializers.get_public_serializer_formats() 47 48 self.assertNotIn('xml', public_formats) 49 self.assertIn('json3', public_formats) 50 51 def test_builtin_serializers(self): 52 "Requesting a list of serializer formats popuates the registry" 53 all_formats = set(serializers.get_serializer_formats()) 54 public_formats = set(serializers.get_public_serializer_formats()) 55 56 self.assertIn('xml', all_formats), 57 self.assertIn('xml', public_formats) 58 59 self.assertIn('json2', all_formats) 60 self.assertIn('json2', public_formats) 61 62 self.assertIn('python', all_formats) 63 self.assertNotIn('python', public_formats) 64 65class SerializersTestBase(object): 66 @staticmethod 67 def _comparison_value(value): 68 return value 69 70 def setUp(self): 71 sports = Category.objects.create(name="Sports") 72 music = Category.objects.create(name="Music") 73 op_ed = Category.objects.create(name="Op-Ed") 74 75 self.joe = Author.objects.create(name="Joe") 76 self.jane = Author.objects.create(name="Jane") 77 78 self.a1 = Article( 79 author=self.jane, 80 headline="Poker has no place on ESPN", 81 pub_date=datetime(2006, 6, 16, 11, 00) 82 ) 83 self.a1.save() 84 self.a1.categories = [sports, op_ed] 85 86 self.a2 = Article( 87 author=self.joe, 88 headline="Time to reform copyright", 89 pub_date=datetime(2006, 6, 16, 13, 00, 11, 345) 90 ) 91 self.a2.save() 92 self.a2.categories = [music, op_ed] 93 94 def test_serialize(self): 95 """Tests that basic serialization works.""" 96 serial_str = serializers.serialize(self.serializer_name, 97 Article.objects.all()) 98 self.assertTrue(self._validate_output(serial_str)) 99 100 def test_serializer_roundtrip(self): 101 """Tests that serialized content can be deserialized.""" 102 serial_str = serializers.serialize(self.serializer_name, 103 Article.objects.all()) 104 models = list(serializers.deserialize(self.serializer_name, serial_str)) 105 self.assertEqual(len(models), 2) 106 107 def test_altering_serialized_output(self): 108 """ 109 Tests the ability to create new objects by 110 modifying serialized content. 111 """ 112 old_headline = "Poker has no place on ESPN" 113 new_headline = "Poker has no place on television" 114 serial_str = serializers.serialize(self.serializer_name, 115 Article.objects.all()) 116 serial_str = serial_str.replace(old_headline, new_headline) 117 models = list(serializers.deserialize(self.serializer_name, serial_str)) 118 119 # Prior to saving, old headline is in place 120 self.assertTrue(Article.objects.filter(headline=old_headline)) 121 self.assertFalse(Article.objects.filter(headline=new_headline)) 122 123 for model in models: 124 model.save() 125 126 # After saving, new headline is in place 127 self.assertTrue(Article.objects.filter(headline=new_headline)) 128 self.assertFalse(Article.objects.filter(headline=old_headline)) 129 130 def test_one_to_one_as_pk(self): 131 """ 132 Tests that if you use your own primary key field 133 (such as a OneToOneField), it doesn't appear in the 134 serialized field list - it replaces the pk identifier. 135 """ 136 profile = AuthorProfile(author=self.joe, 137 date_of_birth=datetime(1970,1,1)) 138 profile.save() 139 serial_str = serializers.serialize(self.serializer_name, 140 AuthorProfile.objects.all()) 141 self.assertFalse(self._get_field_values(serial_str, 'author')) 142 143 for obj in serializers.deserialize(self.serializer_name, serial_str): 144 self.assertEqual(obj.object.pk, self._comparison_value(self.joe.pk)) 145 146 def test_serialize_field_subset(self): 147 """Tests that output can be restricted to a subset of fields""" 148 valid_fields = ('headline','pub_date') 149 invalid_fields = ("author", "categories") 150 serial_str = serializers.serialize(self.serializer_name, 151 Article.objects.all(), 152 fields=valid_fields) 153 for field_name in invalid_fields: 154 self.assertFalse(self._get_field_values(serial_str, field_name)) 155 156 for field_name in valid_fields: 157 self.assertTrue(self._get_field_values(serial_str, field_name)) 158 159 def test_serialize_unicode(self): 160 """Tests that unicode makes the roundtrip intact""" 161 actor_name = u"Za\u017c\u00f3\u0142\u0107" 162 movie_title = u'G\u0119\u015bl\u0105 ja\u017a\u0144' 163 ac = Actor(name=actor_name) 164 mv = Movie(title=movie_title, actor=ac) 165 ac.save() 166 mv.save() 167 168 serial_str = serializers.serialize(self.serializer_name, [mv]) 169 self.assertEqual(self._get_field_values(serial_str, "title")[0], movie_title) 170 self.assertEqual(self._get_field_values(serial_str, "actor")[0], actor_name) 171 172 obj_list = list(serializers.deserialize(self.serializer_name, serial_str)) 173 mv_obj = obj_list[0].object 174 self.assertEqual(mv_obj.title, movie_title) 175 176 def test_serialize_with_null_pk(self): 177 """ 178 Tests that serialized data with no primary key results 179 in a model instance with no id 180 """ 181 category = Category(name="Reference") 182 serial_str = serializers.serialize(self.serializer_name, [category]) 183 pk_value = self._get_pk_values(serial_str)[0] 184 self.assertFalse(pk_value) 185 186 cat_obj = list(serializers.deserialize(self.serializer_name, 187 serial_str))[0].object 188 self.assertEqual(cat_obj.id, None) 189 190 def test_float_serialization(self): 191 """Tests that float values serialize and deserialize intact""" 192 sc = Score(score=3.4) 193 sc.save() 194 serial_str = serializers.serialize(self.serializer_name, [sc]) 195 deserial_objs = list(serializers.deserialize(self.serializer_name, 196 serial_str)) 197 self.assertEqual(deserial_objs[0].object.score, Approximate(3.4, places=1)) 198 199 def test_custom_field_serialization(self): 200 """Tests that custom fields serialize and deserialize intact""" 201 team_str = "Spartak Moskva" 202 player = Player() 203 player.name = "Soslan Djanaev" 204 player.rank = 1 205 player.team = Team(team_str) 206 player.save() 207 serial_str = serializers.serialize(self.serializer_name, 208 Player.objects.all()) 209 team = self._get_field_values(serial_str, "team") 210 self.assertTrue(team) 211 self.assertEqual(team[0], team_str) 212 213 deserial_objs = list(serializers.deserialize(self.serializer_name, serial_str)) 214 self.assertEqual(deserial_objs[0].object.team.to_string(), 215 player.team.to_string()) 216 217 def test_pre_1000ad_date(self): 218 """Tests that year values before 1000AD are properly formatted""" 219 # Regression for #12524 -- dates before 1000AD get prefixed 220 # 0's on the year 221 a = Article.objects.create( 222 author = self.jane, 223 headline = "Nobody remembers the early years", 224 pub_date = datetime(1, 2, 3, 4, 5, 6)) 225 226 serial_str = serializers.serialize(self.serializer_name, [a]) 227 date_values = self._get_field_values(serial_str, "pub_date") 228 self.assertEqual(date_values[0], "0001-02-03 04:05:06") 229 230 def test_pkless_serialized_strings(self): 231 """ 232 Tests that serialized strings without PKs 233 can be turned into models 234 """ 235 deserial_objs = list(serializers.deserialize(self.serializer_name, 236 self.pkless_str)) 237 for obj in deserial_objs: 238 self.assertFalse(obj.object.id) 239 obj.save() 240 self.assertEqual(Category.objects.all().count(), 4) 241 242 243class SerializersTransactionTestBase(object): 244 def test_forward_refs(self): 245 """ 246 Tests that objects ids can be referenced before they are 247 defined in the serialization data. 248 """ 249 # The deserialization process needs to be contained 250 # within a transaction in order to test forward reference 251 # handling. 252 transaction.enter_transaction_management() 253 transaction.managed(True) 254 objs = serializers.deserialize(self.serializer_name, self.fwd_ref_str) 255 for obj in objs: 256 obj.save() 257 transaction.commit() 258 transaction.leave_transaction_management() 259 260 for model_cls in (Category, Author, Article): 261 self.assertEqual(model_cls.objects.all().count(), 1) 262 art_obj = Article.objects.all()[0] 263 self.assertEqual(art_obj.categories.all().count(), 1) 264 self.assertEqual(art_obj.author.name, "Agnes") 265 266 267class XmlSerializerTestCase(SerializersTestBase, TestCase): 268 serializer_name = "xml" 269 pkless_str = """<?xml version="1.0" encoding="utf-8"?> 270<django-objects version="1.0"> 271 <object model="serializers.category"> 272 <field type="CharField" name="name">Reference</field> 273 </object> 274</django-objects>""" 275 276 @staticmethod 277 def _comparison_value(value): 278 # The XML serializer handles everything as strings, so comparisons 279 # need to be performed on the stringified value 280 return unicode(value) 281 282 @staticmethod 283 def _validate_output(serial_str): 284 try: 285 minidom.parseString(serial_str) 286 except Exception: 287 return False 288 else: 289 return True 290 291 @staticmethod 292 def _get_pk_values(serial_str): 293 ret_list = [] 294 dom = minidom.parseString(serial_str) 295 fields = dom.getElementsByTagName("object") 296 for field in fields: 297 ret_list.append(field.getAttribute("pk")) 298 return ret_list 299 300 @staticmethod 301 def _get_field_values(serial_str, field_name): 302 ret_list = [] 303 dom = minidom.parseString(serial_str) 304 fields = dom.getElementsByTagName("field") 305 for field in fields: 306 if field.getAttribute("name") == field_name: 307 temp = [] 308 for child in field.childNodes: 309 temp.append(child.nodeValue) 310 ret_list.append("".join(temp)) 311 return ret_list 312 313class XmlSerializerTransactionTestCase(SerializersTransactionTestBase, TransactionTestCase): 314 serializer_name = "xml" 315 fwd_ref_str = """<?xml version="1.0" encoding="utf-8"?> 316<django-objects version="1.0"> 317 <object pk="1" model="serializers.article"> 318 <field to="serializers.author" name="author" rel="ManyToOneRel">1</field> 319 <field type="CharField" name="headline">Forward references pose no problem</field> 320 <field type="DateTimeField" name="pub_date">2006-06-16 15:00:00</field> 321 <field to="serializers.category" name="categories" rel="ManyToManyRel"> 322 <object pk="1"></object> 323 </field> 324 </object> 325 <object pk="1" model="serializers.author"> 326 <field type="CharField" name="name">Agnes</field> 327 </object> 328 <object pk="1" model="serializers.category"> 329 <field type="CharField" name="name">Reference</field></object> 330</django-objects>""" 331 332 333class JsonSerializerTestCase(SerializersTestBase, TestCase): 334 serializer_name = "json" 335 pkless_str = """[{"pk": null, "model": "serializers.category", "fields": {"name": "Reference"}}]""" 336 337 @staticmethod 338 def _validate_output(serial_str): 339 try: 340 simplejson.loads(serial_str) 341 except Exception: 342 return False 343 else: 344 return True 345 346 @staticmethod 347 def _get_pk_values(serial_str): 348 ret_list = [] 349 serial_list = simplejson.loads(serial_str) 350 for obj_dict in serial_list: 351 ret_list.append(obj_dict["pk"]) 352 return ret_list 353 354 @staticmethod 355 def _get_field_values(serial_str, field_name): 356 ret_list = [] 357 serial_list = simplejson.loads(serial_str) 358 for obj_dict in serial_list: 359 if field_name in obj_dict["fields"]: 360 ret_list.append(obj_dict["fields"][field_name]) 361 return ret_list 362 363class JsonSerializerTransactionTestCase(SerializersTransactionTestBase, TransactionTestCase): 364 serializer_name = "json" 365 fwd_ref_str = """[ 366 { 367 "pk": 1, 368 "model": "serializers.article", 369 "fields": { 370 "headline": "Forward references pose no problem", 371 "pub_date": "2006-06-16 15:00:00", 372 "categories": [1], 373 "author": 1 374 } 375 }, 376 { 377 "pk": 1, 378 "model": "serializers.category", 379 "fields": { 380 "name": "Reference" 381 } 382 }, 383 { 384 "pk": 1, 385 "model": "serializers.author", 386 "fields": { 387 "name": "Agnes" 388 } 389 }]""" 390 391try: 392 import yaml 393except ImportError: 394 pass 395else: 396 class YamlSerializerTestCase(SerializersTestBase, TestCase): 397 serializer_name = "yaml" 398 fwd_ref_str = """- fields: 399 headline: Forward references pose no problem 400 pub_date: 2006-06-16 15:00:00 401 categories: [1] 402 author: 1 403 pk: 1 404 model: serializers.article 405- fields: 406 name: Reference 407 pk: 1 408 model: serializers.category 409- fields: 410 name: Agnes 411 pk: 1 412 model: serializers.author""" 413 414 pkless_str = """- fields: 415 name: Reference 416 pk: null 417 model: serializers.category""" 418 419 @staticmethod 420 def _validate_output(serial_str): 421 try: 422 yaml.load(StringIO(serial_str)) 423 except Exception: 424 return False 425 else: 426 return True 427 428 @staticmethod 429 def _get_pk_values(serial_str): 430 ret_list = [] 431 stream = StringIO(serial_str) 432 for obj_dict in yaml.load(stream): 433 ret_list.append(obj_dict["pk"]) 434 return ret_list 435 436 @staticmethod 437 def _get_field_values(serial_str, field_name): 438 ret_list = [] 439 stream = StringIO(serial_str) 440 for obj_dict in yaml.load(stream): 441 if "fields" in obj_dict and field_name in obj_dict["fields"]: 442 field_value = obj_dict["fields"][field_name] 443 # yaml.load will return non-string objects for some 444 # of the fields we are interested in, this ensures that 445 # everything comes back as a string 446 if isinstance(field_value, basestring): 447 ret_list.append(field_value) 448 else: 449 ret_list.append(str(field_value)) 450 return ret_list 451 452 class YamlSerializerTransactionTestCase(SerializersTransactionTestBase, TransactionTestCase): 453 serializer_name = "yaml" 454 fwd_ref_str = """- fields: 455 headline: Forward references pose no problem 456 pub_date: 2006-06-16 15:00:00 457 categories: [1] 458 author: 1 459 pk: 1 460 model: serializers.article 461- fields: 462 name: Reference 463 pk: 1 464 model: serializers.category 465- fields: 466 name: Agnes 467 pk: 1 468 model: serializers.author"""