Commit 12716794 authored by Russell Keith-Magee's avatar Russell Keith-Magee
Browse files

Fixed #7350, #7202 -- Fixed serialization for multi-model inheritance, which had multiple problems:

 * Serializers were including all superclass fields in their output. Now only local fields are included.
 * Implicit OneToOne primary keys were not correctly added to the metamodel, so they were always marked to be serialized, even though they were primary
 * Model saving was too aggressive about creating new parent class instances during deserialization. Raw save on a model now skips saving of the parent class.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@7600 bcc190cf-cafb-0310-a4f2-bffc1f526a37
parent 1426c245
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -38,7 +38,7 @@ class Serializer(object):
        self.start_serialization()
        for obj in queryset:
            self.start_object(obj)
            for field in obj._meta.fields:
            for field in obj._meta.local_fields:
                if field.serialize:
                    if field.rel is None:
                        if self.selected_fields is None or field.attname in self.selected_fields:
+9 −4
Original line number Diff line number Diff line
@@ -290,6 +290,11 @@ class Model(object):
            meta = cls._meta
            signal = False

        # If we are in a raw save, save the object exactly as presented.
        # That means that we don't try to be smart about saving attributes
        # that might have come from the parent class - we just save the 
        # attributes we have been given to the class we have been given.
        if not raw:
            for parent, field in meta.parents.items():
                self.save_base(raw, parent)
                setattr(self, field.attname, self._get_pk_val(parent._meta))
+1 −1
Original line number Diff line number Diff line
@@ -102,7 +102,7 @@ class Options(object):
                # field.
                field = self.parents.value_for_index(0)
                field.primary_key = True
                self.pk = field
                self.setup_pk(field)
            else:
                auto = AutoField(verbose_name='ID', primary_key=True,
                        auto_created=True)
+35 −0
Original line number Diff line number Diff line
@@ -63,6 +63,41 @@ be serialized.
    doesn't specify all the fields that are required by a model, the deserializer
    will not be able to save deserialized instances.

Inherited Models
~~~~~~~~~~~~~~~~

If you have a model that is defined using an `abstract base class`_, you don't
have to do anything special to serialize that model. Just call the serializer
on the object (or objects) that you want to serialize, and the output will be
a complete representation of the serialized object.

However, if you have a model that uses `multi-table inheritance`_, you also
need to serialize all of the base classes for the model. This is because only
the fields that are locally defined on the model will be serialized. For
example, consider the following models::
	
	class Place(models.Model):
		name = models.CharField(max_length=50)
		
	class Restaurant(Place):
		serves_hot_dogs = models.BooleanField()
		
If you only serialize the Restaurant model::

	data = serializers.serialize('xml', Restaurant.objects.all())

the fields on the serialized output will only contain the `serves_hot_dogs`
attribute. The `name` attribute of the base class will be ignored.

In order to fully serialize your Restaurant instances, you will need to
serialize the Place models as well::

	all_objects = list(Restaurant.objects.all()) + list(Place.objects.all())
	data = serializers.serialize('xml', all_objects)

.. _abstract base class: http://www.djangoproject.com/documentation/model-api/#abstract-base-classes
.. _multi-table inheritance: http://www.djangoproject.com/documentation/model-api/#multi-table-inheritance

Deserializing data
------------------

+5 −0
Original line number Diff line number Diff line
@@ -147,8 +147,13 @@ Test constructor for Restaurant.
>>> c.save()
>>> ir = ItalianRestaurant(name='Ristorante Miron', address='1234 W. Ash', serves_hot_dogs=False, serves_pizza=False, serves_gnocchi=True, rating=4, chef=c)
>>> ir.save()
>>> ItalianRestaurant.objects.filter(address='1234 W. Ash')
[<ItalianRestaurant: Ristorante Miron the italian restaurant>]

>>> ir.address = '1234 W. Elm'
>>> ir.save()
>>> ItalianRestaurant.objects.filter(address='1234 W. Elm')
[<ItalianRestaurant: Ristorante Miron the italian restaurant>]

# Make sure Restaurant and ItalianRestaurant have the right fields in the right
# order.
Loading