diff --git a/import_export/widgets.py b/import_export/widgets.py index 15b51c5..a6b7e9d 100644 --- a/import_export/widgets.py +++ b/import_export/widgets.py @@ -7,6 +7,7 @@ from django.utils import datetime_safe, timezone, six from django.utils.encoding import smart_text from django.utils.dateparse import parse_duration from django.conf import settings +from django.core.exceptions import ObjectDoesNotExist try: from django.utils.encoding import force_text @@ -343,7 +344,19 @@ class ForeignKeyWidget(Widget): def render(self, value, obj=None): if value is None: return "" - return getattr(value, self.field) + + attrs = self.field.split('__') + for attr in attrs: + try: + value = getattr(value, attr, None) + except (ValueError, ObjectDoesNotExist): + # needs to have a primary key value before a many-to-many + # relationship can be used. + return None + if value is None: + return None + + return value class ManyToManyWidget(Widget): diff --git a/tests/core/migrations/0007_auto_20180628_0411.py b/tests/core/migrations/0007_auto_20180628_0411.py new file mode 100644 index 0000000..5d8cca7 --- /dev/null +++ b/tests/core/migrations/0007_auto_20180628_0411.py @@ -0,0 +1,34 @@ +# Generated by Django 2.0.2 on 2018-06-28 04:11 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('core', '0006_auto_20171130_0147'), + ] + + operations = [ + migrations.CreateModel( + name='Person', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ], + ), + migrations.CreateModel( + name='Role', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('user', models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + ), + migrations.AddField( + model_name='person', + name='role', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='core.Role'), + ), + ] diff --git a/tests/core/models.py b/tests/core/models.py index 97a3211..6e34dab 100644 --- a/tests/core/models.py +++ b/tests/core/models.py @@ -68,6 +68,14 @@ class Entry(models.Model): user = models.ForeignKey('auth.User', on_delete=models.CASCADE) +class Role(models.Model): + user = models.OneToOneField('auth.User', on_delete=models.CASCADE, null=True) + + +class Person(models.Model): + role = models.ForeignKey(Role, on_delete=models.CASCADE) + + class WithDefault(models.Model): name = models.CharField('Default', max_length=75, blank=True, default='foo_bar') diff --git a/tests/core/tests/test_resources.py b/tests/core/tests/test_resources.py index b27456b..c1e7b36 100644 --- a/tests/core/tests/test_resources.py +++ b/tests/core/tests/test_resources.py @@ -21,7 +21,7 @@ from import_export.resources import Diff from ..models import ( Author, Book, Category, Entry, Profile, WithDefault, WithDynamicDefault, - WithFloatField, + WithFloatField, Person, Role ) try: @@ -935,6 +935,38 @@ if 'postgresql' in settings.DATABASES['default']['ENGINE']: self.assertListEqual(book_with_chapters.chapters, chapters) +class ForeignKeyWidgetFollowRelationship(TestCase): + def setUp(self): + self.user = User.objects.create(username='foo') + self.role = Role.objects.create(user=self.user) + self.person = Person.objects.create(role=self.role) + + def test_export(self): + class MyPersonResource(resources.ModelResource): + role = fields.Field( + column_name='role', + attribute='role', + widget=widgets.ForeignKeyWidget(Role, field='user__username') + ) + + class Meta: + model = Person + fields = ['id', 'role'] + + resource = MyPersonResource() + dataset = resource.export(Person.objects.all()) + self.assertEqual(len(dataset), 1) + self.assertEqual(dataset[0][0], 'foo') + + self.role.user = None + self.role.save() + + resource = MyPersonResource() + dataset = resource.export(Person.objects.all()) + self.assertEqual(len(dataset), 1) + self.assertEqual(dataset[0][0], None) + + class ManyRelatedManagerDiffTest(TestCase): fixtures = ["category", "book"]