Fixed #24607 -- Serialized natural keys in multi-table inheritance models.
This commit is contained in:
parent
794f866cec
commit
74a575eb72
|
@ -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.
|
||||||
|
|
|
@ -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):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
|
@ -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)
|
||||||
|
|
Loading…
Reference in New Issue