Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion rest_framework/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -1167,7 +1167,7 @@ def build_relational_field(self, field_name, relation_info):
field_kwargs = get_relation_kwargs(field_name, relation_info)

to_field = field_kwargs.pop('to_field', None)
if to_field and not relation_info.related_model._meta.get_field(to_field).primary_key:
if to_field and not relation_info.reverse and not relation_info.related_model._meta.get_field(to_field).primary_key:
field_kwargs['slug_field'] = to_field
field_class = self.serializer_related_to_field

Expand Down
2 changes: 1 addition & 1 deletion rest_framework/utils/field_mapping.py
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ def get_relation_kwargs(field_name, relation_info):
"""
Creates a default instance of a flat relational field.
"""
model_field, related_model, to_many, to_field, has_through_model = relation_info
model_field, related_model, to_many, to_field, has_through_model, reverse = relation_info
kwargs = {
'queryset': related_model._default_manager,
'view_name': get_detail_view_name(related_model)
Expand Down
15 changes: 10 additions & 5 deletions rest_framework/utils/model_meta.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@
'related_model',
'to_many',
'to_field',
'has_through_model'
'has_through_model',
'reverse'
])


Expand Down Expand Up @@ -81,7 +82,8 @@ def _get_forward_relationships(opts):
related_model=get_related_model(field),
to_many=False,
to_field=_get_to_field(field),
has_through_model=False
has_through_model=False,
reverse=False
)

# Deal with forward many-to-many relationships.
Expand All @@ -94,7 +96,8 @@ def _get_forward_relationships(opts):
to_field=None,
has_through_model=(
not get_remote_field(field).through._meta.auto_created
)
),
reverse=False
)

return forward_relations
Expand All @@ -118,7 +121,8 @@ def _get_reverse_relationships(opts):
related_model=related,
to_many=get_remote_field(relation.field).multiple,
to_field=_get_to_field(relation.field),
has_through_model=False
has_through_model=False,
reverse=True
)

# Deal with reverse many-to-many relationships.
Expand All @@ -135,7 +139,8 @@ def _get_reverse_relationships(opts):
has_through_model=(
(getattr(get_remote_field(relation.field), 'through', None) is not None) and
not get_remote_field(relation.field).through._meta.auto_created
)
),
reverse=True
)

return reverse_relations
Expand Down
68 changes: 68 additions & 0 deletions tests/test_model_serializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,15 @@ class ChoicesModel(models.Model):
choices_field_with_nonstandard_args = models.DecimalField(max_digits=3, decimal_places=1, choices=DECIMAL_CHOICES, verbose_name='A label')


class Issue3674ParentModel(models.Model):
title = models.CharField(max_length=64)


class Issue3674ChildModel(models.Model):
parent = models.ForeignKey(Issue3674ParentModel, related_name='children')
value = models.CharField(primary_key=True, max_length=64)


class TestModelSerializer(TestCase):
def test_create_method(self):
class TestSerializer(serializers.ModelSerializer):
Expand Down Expand Up @@ -996,3 +1005,62 @@ class Meta:
fields = TestSerializer().fields
self.assertFalse(fields['field_1'].required)
self.assertTrue(fields['field_2'].required)


class Issue3674Test(TestCase):
def test_nonPK_foreignkey_model_serializer(self):
class TestParentModel(models.Model):
title = models.CharField(max_length=64)

class TestChildModel(models.Model):
parent = models.ForeignKey(TestParentModel, related_name='children')
value = models.CharField(primary_key=True, max_length=64)

class TestChildModelSerializer(serializers.ModelSerializer):
class Meta:
model = TestChildModel
fields = ('value', 'parent')

class TestParentModelSerializer(serializers.ModelSerializer):
class Meta:
model = TestParentModel
fields = ('id', 'title', 'children')

parent_expected = dedent("""
TestParentModelSerializer():
id = IntegerField(label='ID', read_only=True)
title = CharField(max_length=64)
children = PrimaryKeyRelatedField(many=True, queryset=TestChildModel.objects.all())
""")
self.assertEqual(unicode_repr(TestParentModelSerializer()), parent_expected)

child_expected = dedent("""
TestChildModelSerializer():
value = CharField(max_length=64, validators=[<UniqueValidator(queryset=TestChildModel.objects.all())>])
parent = PrimaryKeyRelatedField(queryset=TestParentModel.objects.all())
""")
self.assertEqual(unicode_repr(TestChildModelSerializer()), child_expected)

def test_nonID_PK_foreignkey_model_serializer(self):

class TestChildModelSerializer(serializers.ModelSerializer):
class Meta:
model = Issue3674ChildModel
fields = ('value', 'parent')

class TestParentModelSerializer(serializers.ModelSerializer):
class Meta:
model = Issue3674ParentModel
fields = ('id', 'title', 'children')

parent = Issue3674ParentModel.objects.create(title='abc')
child = Issue3674ChildModel.objects.create(value='def', parent=parent)

parent_serializer = TestParentModelSerializer(parent)
child_serializer = TestChildModelSerializer(child)

parent_expected = {'children': ['def'], 'id': 1, 'title': 'abc'}
self.assertEqual(parent_serializer.data, parent_expected)

child_expected = {'parent': 1, 'value': 'def'}
self.assertEqual(child_serializer.data, child_expected)