Ticket #13252: 13252-natural-key-serializing-r14992.diff
File 13252-natural-key-serializing-r14992.diff, 8.8 KB (added by , 14 years ago) |
---|
-
django/core/serializers/xml_serializer.py
42 42 raise base.SerializationError("Non-model object (%s) encountered during serialization" % type(obj)) 43 43 44 44 self.indent(1) 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 } 45 object_data = {"model": smart_unicode(obj._meta)} 46 if not self.use_natural_keys or not hasattr(obj, 'natural_key'): 47 obj_pk = obj._get_pk_val() 48 if obj_pk is not None: 49 object_data['pk'] = smart_unicode(obj_pk) 50 self.xml.startElement("object", object_data) 53 51 54 self.xml.startElement("object", attrs)55 56 52 def end_object(self, obj): 57 53 """ 58 54 Called after handling all fields for an object. … … 173 169 Model = self._get_model_from_node(node, "model") 174 170 175 171 # 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 172 data = {} 173 if node.hasAttribute('pk'): 174 data[Model._meta.pk.attname] = Model._meta.pk.to_python( 175 node.getAttribute('pk')) 181 176 182 data = {Model._meta.pk.attname : Model._meta.pk.to_python(pk)}183 184 177 # Also start building a dict of m2m data (this is saved as 185 178 # {m2m_accessor_attribute : [list_of_related_objects]}) 186 179 m2m_data = {} … … 210 203 value = field.to_python(getInnerText(field_node).strip()) 211 204 data[field.name] = value 212 205 206 obj = base.build_instance(Model, data, self.db) 207 213 208 # Return a DeserializedObject so that the m2m data has a place to live. 214 return base.DeserializedObject( Model(**data), m2m_data)209 return base.DeserializedObject(obj, m2m_data) 215 210 216 211 def _handle_fk_field_node(self, node, field): 217 212 """ -
django/core/serializers/base.py
170 170 # prevent a second (possibly accidental) call to save() from saving 171 171 # the m2m data twice. 172 172 self.m2m_data = None 173 174 def build_instance(Model, data, db): 175 """ 176 Build a model instance. 177 178 If the model instance doesn't have a primary key and the model supports 179 natural keys, try to retrieve it from the database. 180 """ 181 obj = Model(**data) 182 if obj.pk is None and hasattr(Model, 'natural_key') and\ 183 hasattr(Model._default_manager, 'get_by_natural_key'): 184 pk = obj.natural_key() 185 try: 186 obj.pk = Model._default_manager.db_manager(db)\ 187 .get_by_natural_key(*pk).pk 188 except Model.DoesNotExist: 189 pass 190 return obj -
django/core/serializers/python.py
27 27 self._current = {} 28 28 29 29 def end_object(self, obj): 30 self.objects.append({ 31 "model" : smart_unicode(obj._meta), 32 "pk" : smart_unicode(obj._get_pk_val(), strings_only=True), 33 "fields" : self._current 34 }) 30 data = { 31 "model": smart_unicode(obj._meta), 32 "fields": self._current 33 } 34 if not self.use_natural_keys or not hasattr(obj, 'natural_key'): 35 data['pk'] = smart_unicode(obj._get_pk_val(), strings_only=True) 36 self.objects.append(data) 35 37 self._current = None 36 38 37 39 def handle_field(self, obj, field): … … 82 84 for d in object_list: 83 85 # Look up the model and starting build a dict of data for it. 84 86 Model = _get_model(d["model"]) 85 data = {Model._meta.pk.attname : Model._meta.pk.to_python(d["pk"])} 87 data = {} 88 if 'pk' in d: 89 data[Model._meta.pk.attname] = Model._meta.pk.to_python(d['pk']) 86 90 m2m_data = {} 87 91 88 92 # Handle each field … … 127 131 else: 128 132 data[field.name] = field.to_python(field_value) 129 133 130 yield base.DeserializedObject(Model(**data), m2m_data)134 obj = base.build_instance(Model, data, db) 131 135 136 yield base.DeserializedObject(obj, m2m_data) 137 132 138 def _get_model(model_identifier): 133 139 """ 134 140 Helper to look up a model from an "app_label.module_name" string. -
tests/regressiontests/serializers_regress/tests.py
414 414 self.assertEqual(string_data, stream.getvalue()) 415 415 stream.close() 416 416 417 def naturalKeyTest(format, self): 418 book1 = {'isbn13': '978-1590597255', 'title': 'The Definitive Guide to ' 419 'Django: Web Development Done Right'} 420 book2 = {'isbn13':'978-1590599969', 'title': 'Practical Django Projects'} 421 422 # Create the books. 423 adrian = Book.objects.create(**book1) 424 james = Book.objects.create(**book2) 425 426 # Serialize the books. 427 string_data = serializers.serialize(format, Book.objects.all(), indent=2, 428 use_natural_keys=True) 429 430 # Delete one book (to prove that the natural key generation will only 431 # restore the primary keys of books found in the database via the 432 # get_natural_key manager method). 433 james.delete() 434 435 # Deserialize and test. 436 books = list(serializers.deserialize(format, string_data)) 437 self.assertEqual(len(books), 2) 438 self.assertEqual(books[0].object.title, book1['title']) 439 self.assertEqual(books[0].object.pk, adrian.pk) 440 self.assertEqual(books[1].object.title, book2['title']) 441 self.assertEqual(books[1].object.pk, None) 442 417 443 for format in serializers.get_serializer_formats(): 418 444 setattr(SerializerTests, 'test_' + format + '_serializer', curry(serializerTest, format)) 419 445 setattr(SerializerTests, 'test_' + format + '_serializer_fields', curry(fieldsTest, format)) 446 setattr(SerializerTests, 'test_' + format + '_serializer_natural_key', 447 curry(naturalKeyTest, format)) 420 448 if format != 'python': 421 449 setattr(SerializerTests, 'test_' + format + '_serializer_stream', curry(streamTest, format)) -
tests/regressiontests/serializers_regress/models.py
264 264 265 265 def __len__(self): 266 266 return self.data 267 268 #Tests for natural keys. 269 class BookManager(models.Manager): 270 def get_by_natural_key(self, isbn13): 271 return self.get(isbn13=isbn13) 272 273 class Book(models.Model): 274 isbn13 = models.CharField(max_length=14) 275 title = models.CharField(max_length=100) 276 277 objects = BookManager() 278 279 def natural_key(self): 280 return (self.isbn13,) -
docs/topics/serialization.txt
307 307 fields will be effectively unique, you can still use those fields 308 308 as a natural key. 309 309 310 .. versionadded:: 1.3 311 312 Deserialization of objects with no primary key will always check whether the 313 model's manager has a ``get_by_natural_key()`` method and if so, use it to 314 populate the deserialized object's primary key. 315 310 316 Serialization of natural keys 311 317 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 312 318 … … 353 359 natural keys during serialization, but *not* be able to load those 354 360 key values, just don't define the ``get_by_natural_key()`` method. 355 361 362 .. versionadded:: 1.3 363 364 When ``use_natural_keys=True`` is specified, the primary key is no longer 365 provided in the serialized data of this object since it can be calculated 366 during deserialization:: 367 368 ... 369 { 370 "model": "store.person", 371 "fields": { 372 "first_name": "Douglas", 373 "last_name": "Adams", 374 "birth_date": "1952-03-11", 375 } 376 } 377 ... 378 356 379 Dependencies during serialization 357 380 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 358 381