Skip to content

Commit b950b02

Browse files
committed
Merge pull request encode#850 from tomchristie/dynamic-forms
Forms in Broseable API support dynamic serializers based on request method
2 parents 34776da + aea0401 commit b950b02

File tree

2 files changed

+66
-10
lines changed

2 files changed

+66
-10
lines changed

rest_framework/renderers.py

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -336,7 +336,7 @@ def show_form_for_method(self, view, method, request, obj):
336336
return # Cannot use form overloading
337337

338338
try:
339-
view.check_permissions(clone_request(request, method))
339+
view.check_permissions(request)
340340
except exceptions.APIException:
341341
return False # Doesn't have permissions
342342
return True
@@ -372,6 +372,30 @@ def serializer_to_form_fields(self, serializer):
372372

373373
return fields
374374

375+
def _get_form(self, view, method, request):
376+
# We need to impersonate a request with the correct method,
377+
# so that eg. any dynamic get_serializer_class methods return the
378+
# correct form for each method.
379+
restore = view.request
380+
request = clone_request(request, method)
381+
view.request = request
382+
try:
383+
return self.get_form(view, method, request)
384+
finally:
385+
view.request = restore
386+
387+
def _get_raw_data_form(self, view, method, request, media_types):
388+
# We need to impersonate a request with the correct method,
389+
# so that eg. any dynamic get_serializer_class methods return the
390+
# correct form for each method.
391+
restore = view.request
392+
request = clone_request(request, method)
393+
view.request = request
394+
try:
395+
return self.get_raw_data_form(view, method, request, media_types)
396+
finally:
397+
view.request = restore
398+
375399
def get_form(self, view, method, request):
376400
"""
377401
Get a form, possibly bound to either the input or output data.
@@ -465,15 +489,15 @@ def render(self, data, accepted_media_type=None, renderer_context=None):
465489
renderer = self.get_default_renderer(view)
466490
content = self.get_content(renderer, data, accepted_media_type, renderer_context)
467491

468-
put_form = self.get_form(view, 'PUT', request)
469-
post_form = self.get_form(view, 'POST', request)
470-
patch_form = self.get_form(view, 'PATCH', request)
471-
delete_form = self.get_form(view, 'DELETE', request)
472-
options_form = self.get_form(view, 'OPTIONS', request)
492+
put_form = self._get_form(view, 'PUT', request)
493+
post_form = self._get_form(view, 'POST', request)
494+
patch_form = self._get_form(view, 'PATCH', request)
495+
delete_form = self._get_form(view, 'DELETE', request)
496+
options_form = self._get_form(view, 'OPTIONS', request)
473497

474-
raw_data_put_form = self.get_raw_data_form(view, 'PUT', request, media_types)
475-
raw_data_post_form = self.get_raw_data_form(view, 'POST', request, media_types)
476-
raw_data_patch_form = self.get_raw_data_form(view, 'PATCH', request, media_types)
498+
raw_data_put_form = self._get_raw_data_form(view, 'PUT', request, media_types)
499+
raw_data_post_form = self._get_raw_data_form(view, 'POST', request, media_types)
500+
raw_data_patch_form = self._get_raw_data_form(view, 'PATCH', request, media_types)
477501
raw_data_put_or_patch_form = raw_data_put_form or raw_data_patch_form
478502

479503
name = self.get_name(view)

rest_framework/tests/generics.py

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
from django.db import models
33
from django.shortcuts import get_object_or_404
44
from django.test import TestCase
5-
from rest_framework import generics, serializers, status
5+
from rest_framework import generics, renderers, serializers, status
66
from rest_framework.tests.utils import RequestFactory
77
from rest_framework.tests.models import BasicModel, Comment, SlugBasedModel
88
from rest_framework.compat import six
@@ -476,3 +476,35 @@ def test_get_instance_view_will_return_single_object_when_filter_does_not_exclud
476476
response = instance_view(request, pk=1).render()
477477
self.assertEqual(response.status_code, status.HTTP_200_OK)
478478
self.assertEqual(response.data, {'id': 1, 'text': 'foo'})
479+
480+
481+
class TwoFieldModel(models.Model):
482+
field_a = models.CharField(max_length=100)
483+
field_b = models.CharField(max_length=100)
484+
485+
486+
class DynamicSerializerView(generics.ListCreateAPIView):
487+
model = TwoFieldModel
488+
renderer_classes = (renderers.BrowsableAPIRenderer, renderers.JSONRenderer)
489+
490+
def get_serializer_class(self):
491+
if self.request.method == 'POST':
492+
class DynamicSerializer(serializers.ModelSerializer):
493+
class Meta:
494+
model = TwoFieldModel
495+
fields = ('field_b',)
496+
return DynamicSerializer
497+
return super(DynamicSerializerView, self).get_serializer_class()
498+
499+
500+
class TestFilterBackendAppliedToViews(TestCase):
501+
502+
def test_dynamic_serializer_form_in_browsable_api(self):
503+
"""
504+
GET requests to ListCreateAPIView should return filtered list.
505+
"""
506+
view = DynamicSerializerView.as_view()
507+
request = factory.get('/')
508+
response = view(request).render()
509+
self.assertContains(response, 'field_b')
510+
self.assertNotContains(response, 'field_a')

0 commit comments

Comments
 (0)