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

This commit is contained in:
João Sampaio 2016-10-08 15:09:49 -03:00 committed by Tim Graham
parent 794f866cec
commit 74a575eb72
5 changed files with 68 additions and 3 deletions

View File

@ -88,7 +88,7 @@ class Serializer(object):
if self.selected_fields is None or field.attname in self.selected_fields: if self.selected_fields is None or field.attname in self.selected_fields:
self.handle_field(obj, field) self.handle_field(obj, field)
else: else:
if self.selected_fields is None or field.attname[:-3] in self.selected_fields: if self.field_is_selected(field) and self.output_pk_field(obj, field):
self.handle_fk_field(obj, field) self.handle_fk_field(obj, field)
for field in concrete_model._meta.many_to_many: for field in concrete_model._meta.many_to_many:
if field.serialize: if field.serialize:
@ -101,6 +101,12 @@ class Serializer(object):
self.end_serialization() self.end_serialization()
return self.getvalue() return self.getvalue()
def field_is_selected(self, field):
return self.selected_fields is None or field.attname[:-3] in self.selected_fields
def output_pk_field(self, obj, pk_field):
return self.use_natural_primary_keys or pk_field != obj._meta.pk
def start_serialization(self): def start_serialization(self):
""" """
Called when serializing of the queryset starts. Called when serializing of the queryset starts.

View File

@ -13,6 +13,7 @@ from django.db import connections
from django.db.models import Manager from django.db.models import Manager
from django.db.models.fields import AutoField from django.db.models.fields import AutoField
from django.db.models.fields.proxy import OrderWrt from django.db.models.fields.proxy import OrderWrt
from django.db.models.fields.related import OneToOneField
from django.utils import six from django.utils import six
from django.utils.datastructures import ImmutableList, OrderedSet from django.utils.datastructures import ImmutableList, OrderedSet
from django.utils.deprecation import ( from django.utils.deprecation import (
@ -296,7 +297,11 @@ class Options(object):
def setup_pk(self, field): def setup_pk(self, field):
if not self.pk and field.primary_key: if not self.pk and field.primary_key:
self.pk = field self.pk = field
field.serialize = False # If the field is a OneToOneField and it's been marked as PK, then
# this is a multi-table inheritance PK. It needs to be serialized
# to relate the subclass instance to the superclass instance.
if not isinstance(field, OneToOneField):
field.serialize = False
def setup_proxy(self, target): def setup_proxy(self, target):
""" """

View File

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

View File

@ -0,0 +1,22 @@
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)
class Meta:
manager_inheritance_from_future = True

View File

@ -4,7 +4,7 @@ from django.core import serializers
from django.db import connection from django.db import connection
from django.test import TestCase from django.test import TestCase
from .models import FKDataNaturalKey, NaturalKeyAnchor from .models import Child, FKDataNaturalKey, NaturalKeyAnchor
from .tests import register_tests from .tests import register_tests
@ -69,6 +69,37 @@ def natural_key_test(format, self):
self.assertIsNone(books[1].object.pk) self.assertIsNone(books[1].object.pk)
def natural_pk_mti_test(format, self):
"""
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 # Dynamically register tests for each serializer
register_tests(NaturalKeySerializerTests, 'test_%s_natural_key_serializer', natural_key_serializer_test) 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_keys', natural_key_test)
register_tests(NaturalKeySerializerTests, 'test_%s_serializer_natural_pks_mti', natural_pk_mti_test)