Skip to content

Commit 8d0a91b

Browse files
authored
Fix 3674 (encode#4571)
Handle ModelSerializer case for relationships to models with custom pk.
1 parent 88c6c38 commit 8d0a91b

File tree

4 files changed

+80
-7
lines changed

4 files changed

+80
-7
lines changed

rest_framework/serializers.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1167,7 +1167,7 @@ def build_relational_field(self, field_name, relation_info):
11671167
field_kwargs = get_relation_kwargs(field_name, relation_info)
11681168

11691169
to_field = field_kwargs.pop('to_field', None)
1170-
if to_field and not relation_info.related_model._meta.get_field(to_field).primary_key:
1170+
if to_field and not relation_info.reverse and not relation_info.related_model._meta.get_field(to_field).primary_key:
11711171
field_kwargs['slug_field'] = to_field
11721172
field_class = self.serializer_related_to_field
11731173

rest_framework/utils/field_mapping.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,7 @@ def get_relation_kwargs(field_name, relation_info):
238238
"""
239239
Creates a default instance of a flat relational field.
240240
"""
241-
model_field, related_model, to_many, to_field, has_through_model = relation_info
241+
model_field, related_model, to_many, to_field, has_through_model, reverse = relation_info
242242
kwargs = {
243243
'queryset': related_model._default_manager,
244244
'view_name': get_detail_view_name(related_model)

rest_framework/utils/model_meta.py

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@
2323
'related_model',
2424
'to_many',
2525
'to_field',
26-
'has_through_model'
26+
'has_through_model',
27+
'reverse'
2728
])
2829

2930

@@ -81,7 +82,8 @@ def _get_forward_relationships(opts):
8182
related_model=get_related_model(field),
8283
to_many=False,
8384
to_field=_get_to_field(field),
84-
has_through_model=False
85+
has_through_model=False,
86+
reverse=False
8587
)
8688

8789
# Deal with forward many-to-many relationships.
@@ -94,7 +96,8 @@ def _get_forward_relationships(opts):
9496
to_field=None,
9597
has_through_model=(
9698
not get_remote_field(field).through._meta.auto_created
97-
)
99+
),
100+
reverse=False
98101
)
99102

100103
return forward_relations
@@ -118,7 +121,8 @@ def _get_reverse_relationships(opts):
118121
related_model=related,
119122
to_many=get_remote_field(relation.field).multiple,
120123
to_field=_get_to_field(relation.field),
121-
has_through_model=False
124+
has_through_model=False,
125+
reverse=True
122126
)
123127

124128
# Deal with reverse many-to-many relationships.
@@ -135,7 +139,8 @@ def _get_reverse_relationships(opts):
135139
has_through_model=(
136140
(getattr(get_remote_field(relation.field), 'through', None) is not None) and
137141
not get_remote_field(relation.field).through._meta.auto_created
138-
)
142+
),
143+
reverse=True
139144
)
140145

141146
return reverse_relations

tests/test_model_serializer.py

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,15 @@ class ChoicesModel(models.Model):
8989
choices_field_with_nonstandard_args = models.DecimalField(max_digits=3, decimal_places=1, choices=DECIMAL_CHOICES, verbose_name='A label')
9090

9191

92+
class Issue3674ParentModel(models.Model):
93+
title = models.CharField(max_length=64)
94+
95+
96+
class Issue3674ChildModel(models.Model):
97+
parent = models.ForeignKey(Issue3674ParentModel, related_name='children')
98+
value = models.CharField(primary_key=True, max_length=64)
99+
100+
92101
class TestModelSerializer(TestCase):
93102
def test_create_method(self):
94103
class TestSerializer(serializers.ModelSerializer):
@@ -996,3 +1005,62 @@ class Meta:
9961005
fields = TestSerializer().fields
9971006
self.assertFalse(fields['field_1'].required)
9981007
self.assertTrue(fields['field_2'].required)
1008+
1009+
1010+
class Issue3674Test(TestCase):
1011+
def test_nonPK_foreignkey_model_serializer(self):
1012+
class TestParentModel(models.Model):
1013+
title = models.CharField(max_length=64)
1014+
1015+
class TestChildModel(models.Model):
1016+
parent = models.ForeignKey(TestParentModel, related_name='children')
1017+
value = models.CharField(primary_key=True, max_length=64)
1018+
1019+
class TestChildModelSerializer(serializers.ModelSerializer):
1020+
class Meta:
1021+
model = TestChildModel
1022+
fields = ('value', 'parent')
1023+
1024+
class TestParentModelSerializer(serializers.ModelSerializer):
1025+
class Meta:
1026+
model = TestParentModel
1027+
fields = ('id', 'title', 'children')
1028+
1029+
parent_expected = dedent("""
1030+
TestParentModelSerializer():
1031+
id = IntegerField(label='ID', read_only=True)
1032+
title = CharField(max_length=64)
1033+
children = PrimaryKeyRelatedField(many=True, queryset=TestChildModel.objects.all())
1034+
""")
1035+
self.assertEqual(unicode_repr(TestParentModelSerializer()), parent_expected)
1036+
1037+
child_expected = dedent("""
1038+
TestChildModelSerializer():
1039+
value = CharField(max_length=64, validators=[<UniqueValidator(queryset=TestChildModel.objects.all())>])
1040+
parent = PrimaryKeyRelatedField(queryset=TestParentModel.objects.all())
1041+
""")
1042+
self.assertEqual(unicode_repr(TestChildModelSerializer()), child_expected)
1043+
1044+
def test_nonID_PK_foreignkey_model_serializer(self):
1045+
1046+
class TestChildModelSerializer(serializers.ModelSerializer):
1047+
class Meta:
1048+
model = Issue3674ChildModel
1049+
fields = ('value', 'parent')
1050+
1051+
class TestParentModelSerializer(serializers.ModelSerializer):
1052+
class Meta:
1053+
model = Issue3674ParentModel
1054+
fields = ('id', 'title', 'children')
1055+
1056+
parent = Issue3674ParentModel.objects.create(title='abc')
1057+
child = Issue3674ChildModel.objects.create(value='def', parent=parent)
1058+
1059+
parent_serializer = TestParentModelSerializer(parent)
1060+
child_serializer = TestChildModelSerializer(child)
1061+
1062+
parent_expected = {'children': ['def'], 'id': 1, 'title': 'abc'}
1063+
self.assertEqual(parent_serializer.data, parent_expected)
1064+
1065+
child_expected = {'parent': 1, 'value': 'def'}
1066+
self.assertEqual(child_serializer.data, child_expected)

0 commit comments

Comments
 (0)