Skip to content

Commit 9b22f43

Browse files
author
Jannon Frank
committed
fix read_only related field metadata
1 parent 512a6db commit 9b22f43

File tree

2 files changed

+92
-8
lines changed

2 files changed

+92
-8
lines changed

rest_framework/metadata.py

+9-8
Original file line numberDiff line numberDiff line change
@@ -127,13 +127,14 @@ def get_field_info(self, field):
127127
if value is not None and value != '':
128128
field_info[attr] = force_text(value, strings_only=True)
129129

130-
if hasattr(field, 'choices'):
131-
field_info['choices'] = [
132-
{
133-
'value': choice_value,
134-
'display_name': force_text(choice_name, strings_only=True)
135-
}
136-
for choice_value, choice_name in field.choices.items()
137-
]
130+
if not field_info.get('read_only'):
131+
if hasattr(field, 'choices'):
132+
field_info['choices'] = [
133+
{
134+
'value': choice_value,
135+
'display_name': force_text(choice_name, strings_only=True)
136+
}
137+
for choice_value, choice_name in field.choices.items()
138+
]
138139

139140
return field_info

tests/test_metadata.py

+83
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
from __future__ import unicode_literals
2+
from django.db import models
3+
from django.test import TestCase
4+
from django.core.validators import MinValueValidator, MaxValueValidator
25
from rest_framework import exceptions, metadata, serializers, status, views, versioning
36
from rest_framework.request import Request
47
from rest_framework.renderers import BrowsableAPIRenderer
@@ -212,3 +215,83 @@ def test_null_boolean_field_info_type(self):
212215
options = metadata.SimpleMetadata()
213216
field_info = options.get_field_info(serializers.NullBooleanField())
214217
assert field_info['type'] == 'boolean'
218+
219+
220+
class TestModelSerializerMetadata(TestCase):
221+
def test_read_only_primary_key_related_field(self):
222+
"""
223+
On generic views OPTIONS should return an 'actions' key with metadata
224+
on the fields that may be supplied to PUT and POST requests. It should
225+
not fail when a read_only PrimaryKeyRelatedField is present
226+
"""
227+
class Parent(models.Model):
228+
integer_field = models.IntegerField(validators=[MinValueValidator(1), MaxValueValidator(1000)])
229+
children = models.ManyToManyField('Child')
230+
name = models.CharField(max_length=100, blank=True, null=True)
231+
232+
class Child(models.Model):
233+
name = models.CharField(max_length=100)
234+
235+
class ExampleSerializer(serializers.ModelSerializer):
236+
children = serializers.PrimaryKeyRelatedField(read_only=True, many=True)
237+
238+
class Meta:
239+
model = Parent
240+
241+
class ExampleView(views.APIView):
242+
"""Example view."""
243+
def post(self, request):
244+
pass
245+
246+
def get_serializer(self):
247+
return ExampleSerializer()
248+
249+
view = ExampleView.as_view()
250+
response = view(request=request)
251+
expected = {
252+
'name': 'Example',
253+
'description': 'Example view.',
254+
'renders': [
255+
'application/json',
256+
'text/html'
257+
],
258+
'parses': [
259+
'application/json',
260+
'application/x-www-form-urlencoded',
261+
'multipart/form-data'
262+
],
263+
'actions': {
264+
'POST': {
265+
'id': {
266+
'type': 'integer',
267+
'required': False,
268+
'read_only': True,
269+
'label': 'ID'
270+
},
271+
'children': {
272+
'type': 'field',
273+
'required': False,
274+
'read_only': True,
275+
'label': 'Children'
276+
},
277+
'integer_field': {
278+
'type': 'integer',
279+
'required': True,
280+
'read_only': False,
281+
'label': 'Integer field',
282+
'min_value': 1,
283+
'max_value': 1000
284+
},
285+
'name': {
286+
'type': 'string',
287+
'required': False,
288+
'read_only': False,
289+
'label': 'Name',
290+
'max_length': 100
291+
}
292+
}
293+
}
294+
}
295+
296+
assert response.status_code == status.HTTP_200_OK
297+
assert response.data == expected

0 commit comments

Comments
 (0)