From 10de2a837f580d76a480a9e0b974cecd2d1d26cb Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Thu, 18 Mar 2010 15:22:15 +0000 Subject: [PATCH] Fixed #13030 -- Corrected natural key deserialization to subclasses. Thanks to yishaibeeri for the report and test case. git-svn-id: http://code.djangoproject.com/svn/django/trunk@12804 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- django/core/serializers/python.py | 4 ++ django/core/serializers/xml_serializer.py | 4 ++ .../fixtures/nk-inheritance.json | 18 +++++++ .../fixtures/nk-inheritance2.xml | 23 ++++++++ .../fixtures_regress/models.py | 52 ++++++++++++++++++- 5 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 tests/regressiontests/fixtures_regress/fixtures/nk-inheritance.json create mode 100644 tests/regressiontests/fixtures_regress/fixtures/nk-inheritance2.xml diff --git a/django/core/serializers/python.py b/django/core/serializers/python.py index 3defb6aa27..a68ea21f78 100644 --- a/django/core/serializers/python.py +++ b/django/core/serializers/python.py @@ -111,6 +111,10 @@ def Deserializer(object_list, **options): if hasattr(field_value, '__iter__'): obj = field.rel.to._default_manager.db_manager(db).get_by_natural_key(*field_value) value = getattr(obj, field.rel.field_name) + # If this is a natural foreign key to an object that + # has a FK/O2O as the foreign key, use the FK value + if field.rel.to._meta.pk.rel: + value = value.pk else: value = field.rel.to._meta.get_field(field.rel.field_name).to_python(field_value) data[field.attname] = value diff --git a/django/core/serializers/xml_serializer.py b/django/core/serializers/xml_serializer.py index b0d8d49397..5fef3b6936 100644 --- a/django/core/serializers/xml_serializer.py +++ b/django/core/serializers/xml_serializer.py @@ -221,6 +221,10 @@ class Deserializer(base.Deserializer): field_value = [getInnerText(k).strip() for k in keys] obj = field.rel.to._default_manager.db_manager(self.db).get_by_natural_key(*field_value) obj_pk = getattr(obj, field.rel.field_name) + # If this is a natural foreign key to an object that + # has a FK/O2O as the foreign key, use the FK value + if field.rel.to._meta.pk.rel: + obj_pk = obj_pk.pk else: # Otherwise, treat like a normal PK field_value = getInnerText(node).strip() diff --git a/tests/regressiontests/fixtures_regress/fixtures/nk-inheritance.json b/tests/regressiontests/fixtures_regress/fixtures/nk-inheritance.json new file mode 100644 index 0000000000..08e5d4feee --- /dev/null +++ b/tests/regressiontests/fixtures_regress/fixtures/nk-inheritance.json @@ -0,0 +1,18 @@ +[ + { + "pk": 1, + "model": "fixtures_regress.nkchild", + "fields": { + "data": "apple" + } + }, + { + "pk": 1, + "model": "fixtures_regress.reftonkchild", + "fields": { + "text": "my text", + "nk_fk" : ["apple"], + "nk_m2m": [["apple"]] + } + } +] diff --git a/tests/regressiontests/fixtures_regress/fixtures/nk-inheritance2.xml b/tests/regressiontests/fixtures_regress/fixtures/nk-inheritance2.xml new file mode 100644 index 0000000000..7eb17a6b7e --- /dev/null +++ b/tests/regressiontests/fixtures_regress/fixtures/nk-inheritance2.xml @@ -0,0 +1,23 @@ + + + + james + + + banana + + + other text + + apple + + + + banana + + + apple + + + + \ No newline at end of file diff --git a/tests/regressiontests/fixtures_regress/models.py b/tests/regressiontests/fixtures_regress/models.py index da8818cd4a..0e727c4935 100644 --- a/tests/regressiontests/fixtures_regress/models.py +++ b/tests/regressiontests/fixtures_regress/models.py @@ -52,6 +52,9 @@ class Absolute(models.Model): class Parent(models.Model): name = models.CharField(max_length=10) + class Meta: + ordering = ('id',) + class Child(Parent): data = models.CharField(max_length=10) @@ -130,6 +133,32 @@ class Book(models.Model): ', '.join(s.name for s in self.stores.all()) ) +class NKManager(models.Manager): + def get_by_natural_key(self, data): + return self.get(data=data) + +class NKChild(Parent): + data = models.CharField(max_length=10, unique=True) + objects = NKManager() + + def natural_key(self): + return self.data + + def __unicode__(self): + return u'NKChild %s:%s' % (self.name, self.data) + +class RefToNKChild(models.Model): + text = models.CharField(max_length=10) + nk_fk = models.ForeignKey(NKChild, related_name='ref_fks') + nk_m2m = models.ManyToManyField(NKChild, related_name='ref_m2ms') + + def __unicode__(self): + return u'%s: Reference to %s [%s]' % ( + self.text, + self.nk_fk, + ', '.join(str(o) for o in self.nk_m2m.all()) + ) + # ome models with pathological circular dependencies class Circle1(models.Model): name = models.CharField(max_length=255) @@ -244,6 +273,27 @@ No fixture data found for 'bad_fixture2'. (File format may be invalid.) >>> management.call_command('loaddata', 'model-inheritance.json', verbosity=0) +############################################### +# Test for ticket #13030 -- natural keys deserialize with fk to inheriting model + +# load data with natural keys +>>> management.call_command('loaddata', 'nk-inheritance.json', verbosity=0) + +>>> NKChild.objects.get(pk=1) + + +>>> RefToNKChild.objects.get(pk=1) + + +# ... and again in XML +>>> management.call_command('loaddata', 'nk-inheritance2.xml', verbosity=0) + +>>> NKChild.objects.get(pk=2) + + +>>> RefToNKChild.objects.get(pk=2) + + ############################################### # Test for ticket #7572 -- MySQL has a problem if the same connection is # used to create tables, load data, and then query over that data. @@ -279,7 +329,7 @@ Weight = 1.2 () [{"pk": 1, "model": "fixtures_regress.animal", "fields": {"count": 3, "weight": 1.2, "name": "Lion", "latin_name": "Panthera leo"}}, {"pk": 2, "model": "fixtures_regress.animal", "fields": {"count": 2, "weight": 2.2, "name": "Platypus", "latin_name": "Ornithorhynchus anatinus"}}, {"pk": 10, "model": "fixtures_regress.animal", "fields": {"count": 42, "weight": 1.2, "name": "Emu", "latin_name": "Dromaius novaehollandiae"}}] ############################################### -# Regression for #11428 - Proxy models aren't included when you dumpdata +# Regression for #11428 - Proxy models aren't included when you dumpdata # Flush out the database first >>> management.call_command('reset', 'fixtures_regress', interactive=False, verbosity=0)