Fixed #24607 -- Serialized natural keys in multi-table inheritance models.

Thanks João Paulo Melo de Sampaio for the test.
This commit is contained in:
Denys Duchier 2017-02-11 06:40:15 -05:00 committed by Tim Graham
parent 9ba3df8240
commit cb7860cced
4 changed files with 57 additions and 2 deletions

View File

@ -82,8 +82,16 @@ class Serializer:
# Use the concrete parent class' _meta instead of the object's _meta
# This is to avoid local_fields problems for proxy models. Refs #17717.
concrete_model = obj._meta.concrete_model
# When using natural primary keys, retrieve the pk field of the
# parent for multi-table inheritance child models. That field must
# be serialized, otherwise deserialization isn't possible.
if self.use_natural_primary_keys:
pk = concrete_model._meta.pk
pk_parent = pk if pk.remote_field and pk.remote_field.parent_link else None
else:
pk_parent = None
for field in concrete_model._meta.local_fields:
if field.serialize:
if field.serialize or field is pk_parent:
if field.remote_field is None:
if self.selected_fields is None or field.attname in self.selected_fields:
self.handle_field(obj, field)

View File

@ -1,3 +1,4 @@
from .base import * # NOQA
from .data import * # NOQA
from .multi_table import * # NOQA
from .natural import * # NOQA

View File

@ -0,0 +1,19 @@
from django.db import models
class ParentManager(models.Manager):
def get_by_natural_key(self, parent_data):
return self.get(parent_data=parent_data)
class Parent(models.Model):
parent_data = models.CharField(max_length=30, unique=True)
objects = ParentManager()
def natural_key(self):
return (self.parent_data,)
class Child(Parent):
child_data = models.CharField(max_length=30, unique=True)

View File

@ -2,7 +2,7 @@ from django.core import serializers
from django.db import connection
from django.test import TestCase
from .models import FKDataNaturalKey, NaturalKeyAnchor
from .models import Child, FKDataNaturalKey, NaturalKeyAnchor
from .tests import register_tests
@ -67,6 +67,33 @@ def natural_key_test(self, format):
self.assertIsNone(books[1].object.pk)
def natural_pk_mti_test(self, format):
"""
If serializing objects in a multi-table inheritance relationship using
natural primary keys, the natural foreign key for the parent is output in
the fields of the child so it's possible to relate the child to the parent
when deserializing.
"""
child_1 = Child.objects.create(parent_data='1', child_data='1')
child_2 = Child.objects.create(parent_data='2', child_data='2')
string_data = serializers.serialize(
format,
[child_1.parent_ptr, child_2.parent_ptr, child_2, child_1],
use_natural_foreign_keys=True, use_natural_primary_keys=True,
)
child_1.delete()
child_2.delete()
for obj in serializers.deserialize(format, string_data):
obj.save()
children = Child.objects.all()
self.assertEqual(len(children), 2)
for child in children:
# If it's possible to find the superclass from the subclass and it's
# the correct superclass, it's working.
self.assertEqual(child.child_data, child.parent_data)
# Dynamically register tests for each serializer
register_tests(NaturalKeySerializerTests, 'test_%s_natural_key_serializer', natural_key_serializer_test)
register_tests(NaturalKeySerializerTests, 'test_%s_serializer_natural_keys', natural_key_test)
register_tests(NaturalKeySerializerTests, 'test_%s_serializer_natural_pks_mti', natural_pk_mti_test)