From 8e90560aa8868a42bb8eda6273595bf0932a6090 Mon Sep 17 00:00:00 2001 From: Mikolaj Rybinski Date: Thu, 4 Feb 2021 18:19:14 +0100 Subject: [PATCH] Fixed #32420 -- Fixed detecting primary key values in deserialization when PK is also a FK. --- django/core/serializers/base.py | 2 +- tests/serializers/models/natural.py | 14 ++++++++++++++ tests/serializers/test_natural.py | 23 +++++++++++++++++++++-- 3 files changed, 36 insertions(+), 3 deletions(-) diff --git a/django/core/serializers/base.py b/django/core/serializers/base.py index 72565236c4..3fee7893df 100644 --- a/django/core/serializers/base.py +++ b/django/core/serializers/base.py @@ -257,7 +257,7 @@ def build_instance(Model, data, db): natural keys, try to retrieve it from the database. """ default_manager = Model._meta.default_manager - pk = data.get(Model._meta.pk.name) + pk = data.get(Model._meta.pk.attname) if (pk is None and hasattr(default_manager, 'get_by_natural_key') and hasattr(Model, 'natural_key')): natural_key = Model(**data).natural_key() diff --git a/tests/serializers/models/natural.py b/tests/serializers/models/natural.py index 88bfed566b..d062d7a45b 100644 --- a/tests/serializers/models/natural.py +++ b/tests/serializers/models/natural.py @@ -53,3 +53,17 @@ class NaturalPKWithDefault(models.Model): def natural_key(self): return (self.name,) + + +class FKAsPKNoNaturalKeyManager(models.Manager): + def get_by_natural_key(self, *args, **kwargs): + return super().get_by_natural_key(*args, **kwargs) + + +class FKAsPKNoNaturalKey(models.Model): + pk_fk = models.ForeignKey(NaturalKeyAnchor, on_delete=models.CASCADE, primary_key=True) + + objects = FKAsPKNoNaturalKeyManager() + + def natural_key(self): + raise NotImplementedError('This method was not expected to be called.') diff --git a/tests/serializers/test_natural.py b/tests/serializers/test_natural.py index 3b4218bcc9..7d05f3a71f 100644 --- a/tests/serializers/test_natural.py +++ b/tests/serializers/test_natural.py @@ -3,8 +3,8 @@ from django.db import connection from django.test import TestCase from .models import ( - Child, FKDataNaturalKey, NaturalKeyAnchor, NaturalKeyThing, - NaturalPKWithDefault, + Child, FKAsPKNoNaturalKey, FKDataNaturalKey, NaturalKeyAnchor, + NaturalKeyThing, NaturalPKWithDefault, ) from .tests import register_tests @@ -200,6 +200,20 @@ def pk_with_default(self, format): self.assertEqual(objs[0].object.pk, obj.pk) +def fk_as_pk_natural_key_not_called(self, format): + """ + The deserializer doesn't rely on natural keys when a model has a custom + primary key that is a ForeignKey. + """ + o1 = NaturalKeyAnchor.objects.create(data='978-1590599969') + o2 = FKAsPKNoNaturalKey.objects.create(pk_fk=o1) + serialized_data = serializers.serialize(format, [o1, o2]) + deserialized_objects = list(serializers.deserialize(format, serialized_data)) + self.assertEqual(len(deserialized_objects), 2) + for obj in deserialized_objects: + self.assertEqual(obj.object.pk, o1.pk) + + # 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) @@ -209,3 +223,8 @@ register_tests(NaturalKeySerializerTests, 'test_%s_forward_references_fk_errors' register_tests(NaturalKeySerializerTests, 'test_%s_forward_references_m2ms', forward_ref_m2m_test) register_tests(NaturalKeySerializerTests, 'test_%s_forward_references_m2m_errors', forward_ref_m2m_with_error_test) register_tests(NaturalKeySerializerTests, 'test_%s_pk_with_default', pk_with_default) +register_tests( + NaturalKeySerializerTests, + 'test_%s_fk_as_pk_natural_key_not_called', + fk_as_pk_natural_key_not_called, +)