Skip to content

Commit

Permalink
[3.2.x] Fixed #32420 -- Fixed detecting primary key values in deseria…
Browse files Browse the repository at this point in the history
…lization when PK is also a FK.

Backport of 8e90560 from master
  • Loading branch information
Mikolaj Rybinski authored and felixxm committed Feb 5, 2021
1 parent f490cde commit d881a0e
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 3 deletions.
2 changes: 1 addition & 1 deletion django/core/serializers/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
14 changes: 14 additions & 0 deletions tests/serializers/models/natural.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,17 @@ def get_by_natural_key(self, name):

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.')
23 changes: 21 additions & 2 deletions tests/serializers/test_natural.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
from django.test import TestCase

from .models import (
Child, FKDataNaturalKey, NaturalKeyAnchor, NaturalKeyThing,
NaturalPKWithDefault,
Child, FKAsPKNoNaturalKey, FKDataNaturalKey, NaturalKeyAnchor,
NaturalKeyThing, NaturalPKWithDefault,
)
from .tests import register_tests

Expand Down Expand Up @@ -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)
Expand All @@ -209,3 +223,8 @@ def pk_with_default(self, format):
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,
)

0 comments on commit d881a0e

Please sign in to comment.