From dcd1025f4c03faa4a9915a1d47d07008280dd3cf Mon Sep 17 00:00:00 2001 From: "dmytryi.striletskyi" Date: Thu, 22 Nov 2018 22:11:51 +0100 Subject: [PATCH] Fixed #28385 -- Fixed deserializing natural keys when primary key has a default value. Co-Authored-By: Hasan Ramezani --- django/core/serializers/base.py | 15 +++++++++------ tests/serializers/models/natural.py | 16 ++++++++++++++++ tests/serializers/test_natural.py | 21 ++++++++++++++++++++- 3 files changed, 45 insertions(+), 7 deletions(-) diff --git a/django/core/serializers/base.py b/django/core/serializers/base.py index 624fc312bd..a9c6dbf1dc 100644 --- a/django/core/serializers/base.py +++ b/django/core/serializers/base.py @@ -256,15 +256,18 @@ def build_instance(Model, data, db): If the model instance doesn't have a primary key and the model supports natural keys, try to retrieve it from the database. """ - obj = Model(**data) - if (obj.pk is None and hasattr(Model, 'natural_key') and - hasattr(Model._default_manager, 'get_by_natural_key')): - natural_key = obj.natural_key() + default_manager = Model._meta.default_manager + pk = data.get(Model._meta.pk.name) + if (pk is None and hasattr(default_manager, 'get_by_natural_key') and + hasattr(Model, 'natural_key')): + natural_key = Model(**data).natural_key() try: - obj.pk = Model._default_manager.db_manager(db).get_by_natural_key(*natural_key).pk + data[Model._meta.pk.attname] = Model._meta.pk.to_python( + default_manager.db_manager(db).get_by_natural_key(*natural_key).pk + ) except Model.DoesNotExist: pass - return obj + return Model(**data) def deserialize_m2m_values(field, field_value, using, handle_forward_references): diff --git a/tests/serializers/models/natural.py b/tests/serializers/models/natural.py index cd24b67f0a..62cf95185e 100644 --- a/tests/serializers/models/natural.py +++ b/tests/serializers/models/natural.py @@ -1,4 +1,6 @@ """Models for test_natural.py""" +import uuid + from django.db import models @@ -37,3 +39,17 @@ class NaturalKeyThing(models.Model): def __str__(self): return self.key + + +class NaturalPKWithDefault(models.Model): + id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) + name = models.CharField(max_length=100, unique=True) + + class Manager(models.Manager): + def get_by_natural_key(self, name): + return self.get(name=name) + + objects = Manager() + + def natural_key(self): + return (self.name,) diff --git a/tests/serializers/test_natural.py b/tests/serializers/test_natural.py index 7e9e9382da..3b4218bcc9 100644 --- a/tests/serializers/test_natural.py +++ b/tests/serializers/test_natural.py @@ -2,7 +2,10 @@ from django.core import serializers from django.db import connection from django.test import TestCase -from .models import Child, FKDataNaturalKey, NaturalKeyAnchor, NaturalKeyThing +from .models import ( + Child, FKDataNaturalKey, NaturalKeyAnchor, NaturalKeyThing, + NaturalPKWithDefault, +) from .tests import register_tests @@ -182,6 +185,21 @@ def forward_ref_m2m_with_error_test(self, format): obj.save_deferred_fields() +def pk_with_default(self, format): + """ + The deserializer works with natural keys when the primary key has a default + value. + """ + obj = NaturalPKWithDefault.objects.create(name='name') + string_data = serializers.serialize( + format, NaturalPKWithDefault.objects.all(), use_natural_foreign_keys=True, + use_natural_primary_keys=True, + ) + objs = list(serializers.deserialize(format, string_data)) + self.assertEqual(len(objs), 1) + self.assertEqual(objs[0].object.pk, obj.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) @@ -190,3 +208,4 @@ register_tests(NaturalKeySerializerTests, 'test_%s_forward_references_fks', forw register_tests(NaturalKeySerializerTests, 'test_%s_forward_references_fk_errors', forward_ref_fk_with_error_test) 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)