Fixes BrowsableAPIRenderer for usage with ListSerializer. (#7530)

Renders list of items in raw_data_form and does not renders form in
template while using with `ListSerializer` (`many=True`).
This commit is contained in:
Niyaz 2023-06-12 16:28:28 +03:00 committed by GitHub
parent 376a5cbbba
commit 02d9bfc2dd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 65 additions and 0 deletions

View File

@ -506,6 +506,9 @@ class BrowsableAPIRenderer(BaseRenderer):
return self.render_form_for_serializer(serializer) return self.render_form_for_serializer(serializer)
def render_form_for_serializer(self, serializer): def render_form_for_serializer(self, serializer):
if isinstance(serializer, serializers.ListSerializer):
return None
if hasattr(serializer, 'initial_data'): if hasattr(serializer, 'initial_data'):
serializer.is_valid() serializer.is_valid()
@ -555,10 +558,13 @@ class BrowsableAPIRenderer(BaseRenderer):
context['indent'] = 4 context['indent'] = 4
# strip HiddenField from output # strip HiddenField from output
is_list_serializer = isinstance(serializer, serializers.ListSerializer)
serializer = serializer.child if is_list_serializer else serializer
data = serializer.data.copy() data = serializer.data.copy()
for name, field in serializer.fields.items(): for name, field in serializer.fields.items():
if isinstance(field, serializers.HiddenField): if isinstance(field, serializers.HiddenField):
data.pop(name, None) data.pop(name, None)
data = [data] if is_list_serializer else data
content = renderer.render(data, accepted, context) content = renderer.render(data, accepted, context)
# Renders returns bytes, but CharField expects a str. # Renders returns bytes, but CharField expects a str.
content = content.decode() content = content.decode()

View File

@ -633,6 +633,9 @@ class BrowsableAPIRendererTests(URLPatternsTestCase):
class AuthExampleViewSet(ExampleViewSet): class AuthExampleViewSet(ExampleViewSet):
permission_classes = [permissions.IsAuthenticated] permission_classes = [permissions.IsAuthenticated]
class SimpleSerializer(serializers.Serializer):
name = serializers.CharField()
router = SimpleRouter() router = SimpleRouter()
router.register('examples', ExampleViewSet, basename='example') router.register('examples', ExampleViewSet, basename='example')
router.register('auth-examples', AuthExampleViewSet, basename='auth-example') router.register('auth-examples', AuthExampleViewSet, basename='auth-example')
@ -640,6 +643,62 @@ class BrowsableAPIRendererTests(URLPatternsTestCase):
def setUp(self): def setUp(self):
self.renderer = BrowsableAPIRenderer() self.renderer = BrowsableAPIRenderer()
self.renderer.accepted_media_type = ''
self.renderer.renderer_context = {}
def test_render_form_for_serializer(self):
with self.subTest('Serializer'):
serializer = BrowsableAPIRendererTests.SimpleSerializer(data={'name': 'Name'})
form = self.renderer.render_form_for_serializer(serializer)
assert isinstance(form, str), 'Must return form for serializer'
with self.subTest('ListSerializer'):
list_serializer = BrowsableAPIRendererTests.SimpleSerializer(data=[{'name': 'Name'}], many=True)
form = self.renderer.render_form_for_serializer(list_serializer)
assert form is None, 'Must not return form for list serializer'
def test_get_raw_data_form(self):
with self.subTest('Serializer'):
class DummyGenericViewsetLike(APIView):
def get_serializer(self, **kwargs):
return BrowsableAPIRendererTests.SimpleSerializer(**kwargs)
def get(self, request):
response = Response()
response.view = self
return response
post = get
view = DummyGenericViewsetLike.as_view()
_request = APIRequestFactory().get('/')
request = Request(_request)
response = view(_request)
view = response.view
raw_data_form = self.renderer.get_raw_data_form({'name': 'Name'}, view, 'POST', request)
assert raw_data_form['_content'].initial == '{\n "name": ""\n}'
with self.subTest('ListSerializer'):
class DummyGenericViewsetLike(APIView):
def get_serializer(self, **kwargs):
return BrowsableAPIRendererTests.SimpleSerializer(many=True, **kwargs) # returns ListSerializer
def get(self, request):
response = Response()
response.view = self
return response
post = get
view = DummyGenericViewsetLike.as_view()
_request = APIRequestFactory().get('/')
request = Request(_request)
response = view(_request)
view = response.view
raw_data_form = self.renderer.get_raw_data_form([{'name': 'Name'}], view, 'POST', request)
assert raw_data_form['_content'].initial == '[\n {\n "name": ""\n }\n]'
def test_get_description_returns_empty_string_for_401_and_403_statuses(self): def test_get_description_returns_empty_string_for_401_and_403_statuses(self):
assert self.renderer.get_description({}, status_code=401) == '' assert self.renderer.get_description({}, status_code=401) == ''