mirror of
				https://github.com/encode/django-rest-framework.git
				synced 2025-11-04 09:57:55 +03:00 
			
		
		
		
	Merge pull request #850 from tomchristie/dynamic-forms
Forms in Broseable API support dynamic serializers based on request method
This commit is contained in:
		
						commit
						b950b025bc
					
				| 
						 | 
					@ -336,7 +336,7 @@ class BrowsableAPIRenderer(BaseRenderer):
 | 
				
			||||||
            return  # Cannot use form overloading
 | 
					            return  # Cannot use form overloading
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
            view.check_permissions(clone_request(request, method))
 | 
					            view.check_permissions(request)
 | 
				
			||||||
        except exceptions.APIException:
 | 
					        except exceptions.APIException:
 | 
				
			||||||
            return False  # Doesn't have permissions
 | 
					            return False  # Doesn't have permissions
 | 
				
			||||||
        return True
 | 
					        return True
 | 
				
			||||||
| 
						 | 
					@ -372,6 +372,30 @@ class BrowsableAPIRenderer(BaseRenderer):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return fields
 | 
					        return fields
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _get_form(self, view, method, request):
 | 
				
			||||||
 | 
					        # We need to impersonate a request with the correct method,
 | 
				
			||||||
 | 
					        # so that eg. any dynamic get_serializer_class methods return the
 | 
				
			||||||
 | 
					        # correct form for each method.
 | 
				
			||||||
 | 
					        restore = view.request
 | 
				
			||||||
 | 
					        request = clone_request(request, method)
 | 
				
			||||||
 | 
					        view.request = request
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            return self.get_form(view, method, request)
 | 
				
			||||||
 | 
					        finally:
 | 
				
			||||||
 | 
					            view.request = restore
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _get_raw_data_form(self, view, method, request, media_types):
 | 
				
			||||||
 | 
					        # We need to impersonate a request with the correct method,
 | 
				
			||||||
 | 
					        # so that eg. any dynamic get_serializer_class methods return the
 | 
				
			||||||
 | 
					        # correct form for each method.
 | 
				
			||||||
 | 
					        restore = view.request
 | 
				
			||||||
 | 
					        request = clone_request(request, method)
 | 
				
			||||||
 | 
					        view.request = request
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            return self.get_raw_data_form(view, method, request, media_types)
 | 
				
			||||||
 | 
					        finally:
 | 
				
			||||||
 | 
					            view.request = restore
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def get_form(self, view, method, request):
 | 
					    def get_form(self, view, method, request):
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        Get a form, possibly bound to either the input or output data.
 | 
					        Get a form, possibly bound to either the input or output data.
 | 
				
			||||||
| 
						 | 
					@ -465,15 +489,15 @@ class BrowsableAPIRenderer(BaseRenderer):
 | 
				
			||||||
        renderer = self.get_default_renderer(view)
 | 
					        renderer = self.get_default_renderer(view)
 | 
				
			||||||
        content = self.get_content(renderer, data, accepted_media_type, renderer_context)
 | 
					        content = self.get_content(renderer, data, accepted_media_type, renderer_context)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        put_form = self.get_form(view, 'PUT', request)
 | 
					        put_form = self._get_form(view, 'PUT', request)
 | 
				
			||||||
        post_form = self.get_form(view, 'POST', request)
 | 
					        post_form = self._get_form(view, 'POST', request)
 | 
				
			||||||
        patch_form = self.get_form(view, 'PATCH', request)
 | 
					        patch_form = self._get_form(view, 'PATCH', request)
 | 
				
			||||||
        delete_form = self.get_form(view, 'DELETE', request)
 | 
					        delete_form = self._get_form(view, 'DELETE', request)
 | 
				
			||||||
        options_form = self.get_form(view, 'OPTIONS', request)
 | 
					        options_form = self._get_form(view, 'OPTIONS', request)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        raw_data_put_form = self.get_raw_data_form(view, 'PUT', request, media_types)
 | 
					        raw_data_put_form = self._get_raw_data_form(view, 'PUT', request, media_types)
 | 
				
			||||||
        raw_data_post_form = self.get_raw_data_form(view, 'POST', request, media_types)
 | 
					        raw_data_post_form = self._get_raw_data_form(view, 'POST', request, media_types)
 | 
				
			||||||
        raw_data_patch_form = self.get_raw_data_form(view, 'PATCH', request, media_types)
 | 
					        raw_data_patch_form = self._get_raw_data_form(view, 'PATCH', request, media_types)
 | 
				
			||||||
        raw_data_put_or_patch_form = raw_data_put_form or raw_data_patch_form
 | 
					        raw_data_put_or_patch_form = raw_data_put_form or raw_data_patch_form
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        name = self.get_name(view)
 | 
					        name = self.get_name(view)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,7 +2,7 @@ from __future__ import unicode_literals
 | 
				
			||||||
from django.db import models
 | 
					from django.db import models
 | 
				
			||||||
from django.shortcuts import get_object_or_404
 | 
					from django.shortcuts import get_object_or_404
 | 
				
			||||||
from django.test import TestCase
 | 
					from django.test import TestCase
 | 
				
			||||||
from rest_framework import generics, serializers, status
 | 
					from rest_framework import generics, renderers, serializers, status
 | 
				
			||||||
from rest_framework.tests.utils import RequestFactory
 | 
					from rest_framework.tests.utils import RequestFactory
 | 
				
			||||||
from rest_framework.tests.models import BasicModel, Comment, SlugBasedModel
 | 
					from rest_framework.tests.models import BasicModel, Comment, SlugBasedModel
 | 
				
			||||||
from rest_framework.compat import six
 | 
					from rest_framework.compat import six
 | 
				
			||||||
| 
						 | 
					@ -476,3 +476,35 @@ class TestFilterBackendAppliedToViews(TestCase):
 | 
				
			||||||
        response = instance_view(request, pk=1).render()
 | 
					        response = instance_view(request, pk=1).render()
 | 
				
			||||||
        self.assertEqual(response.status_code, status.HTTP_200_OK)
 | 
					        self.assertEqual(response.status_code, status.HTTP_200_OK)
 | 
				
			||||||
        self.assertEqual(response.data, {'id': 1, 'text': 'foo'})
 | 
					        self.assertEqual(response.data, {'id': 1, 'text': 'foo'})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TwoFieldModel(models.Model):
 | 
				
			||||||
 | 
					    field_a = models.CharField(max_length=100)
 | 
				
			||||||
 | 
					    field_b = models.CharField(max_length=100)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class DynamicSerializerView(generics.ListCreateAPIView):
 | 
				
			||||||
 | 
					    model = TwoFieldModel
 | 
				
			||||||
 | 
					    renderer_classes = (renderers.BrowsableAPIRenderer, renderers.JSONRenderer)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_serializer_class(self):
 | 
				
			||||||
 | 
					        if self.request.method == 'POST':
 | 
				
			||||||
 | 
					            class DynamicSerializer(serializers.ModelSerializer):
 | 
				
			||||||
 | 
					                class Meta:
 | 
				
			||||||
 | 
					                    model = TwoFieldModel
 | 
				
			||||||
 | 
					                    fields = ('field_b',)
 | 
				
			||||||
 | 
					            return DynamicSerializer
 | 
				
			||||||
 | 
					        return super(DynamicSerializerView, self).get_serializer_class()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TestFilterBackendAppliedToViews(TestCase):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_dynamic_serializer_form_in_browsable_api(self):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        GET requests to ListCreateAPIView should return filtered list.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        view = DynamicSerializerView.as_view()
 | 
				
			||||||
 | 
					        request = factory.get('/')
 | 
				
			||||||
 | 
					        response = view(request).render()
 | 
				
			||||||
 | 
					        self.assertContains(response, 'field_b')
 | 
				
			||||||
 | 
					        self.assertNotContains(response, 'field_a')
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue
	
	Block a user