Skip to content

Commit 8d0a91b

Browse files
authored
Fix 3674 (#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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy