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:
parent
9ba3df8240
commit
cb7860cced
|
@ -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)
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
from .base import * # NOQA
|
||||
from .data import * # NOQA
|
||||
from .multi_table import * # NOQA
|
||||
from .natural import * # NOQA
|
||||
|
|
|
@ -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)
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue