mirror of
				https://github.com/encode/django-rest-framework.git
				synced 2025-10-31 16:07:38 +03:00 
			
		
		
		
	
		
			
				
	
	
		
			414 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			414 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| import pytest
 | |
| from django.core.validators import MaxValueValidator, MinValueValidator
 | |
| from django.db import models
 | |
| from django.test import TestCase
 | |
| 
 | |
| from rest_framework import (
 | |
|     exceptions, metadata, serializers, status, versioning, views
 | |
| )
 | |
| from rest_framework.renderers import BrowsableAPIRenderer
 | |
| from rest_framework.test import APIRequestFactory
 | |
| 
 | |
| from .models import BasicModel
 | |
| 
 | |
| request = APIRequestFactory().options('/')
 | |
| 
 | |
| 
 | |
| class TestMetadata:
 | |
| 
 | |
|     def test_determine_metadata_abstract_method_raises_proper_error(self):
 | |
|         with pytest.raises(NotImplementedError):
 | |
|             metadata.BaseMetadata().determine_metadata(None, None)
 | |
| 
 | |
|     def test_metadata(self):
 | |
|         """
 | |
|         OPTIONS requests to views should return a valid 200 response.
 | |
|         """
 | |
|         class ExampleView(views.APIView):
 | |
|             """Example view."""
 | |
|             pass
 | |
| 
 | |
|         view = ExampleView.as_view()
 | |
|         response = view(request=request)
 | |
|         expected = {
 | |
|             'name': 'Example',
 | |
|             'description': 'Example view.',
 | |
|             'renders': [
 | |
|                 'application/json',
 | |
|                 'text/html'
 | |
|             ],
 | |
|             'parses': [
 | |
|                 'application/json',
 | |
|                 'application/x-www-form-urlencoded',
 | |
|                 'multipart/form-data'
 | |
|             ]
 | |
|         }
 | |
|         assert response.status_code == status.HTTP_200_OK
 | |
|         assert response.data == expected
 | |
| 
 | |
|     def test_none_metadata(self):
 | |
|         """
 | |
|         OPTIONS requests to views where `metadata_class = None` should raise
 | |
|         a MethodNotAllowed exception, which will result in an HTTP 405 response.
 | |
|         """
 | |
|         class ExampleView(views.APIView):
 | |
|             metadata_class = None
 | |
| 
 | |
|         view = ExampleView.as_view()
 | |
|         response = view(request=request)
 | |
|         assert response.status_code == status.HTTP_405_METHOD_NOT_ALLOWED
 | |
|         assert response.data == {'detail': 'Method "OPTIONS" not allowed.'}
 | |
| 
 | |
|     def test_actions(self):
 | |
|         """
 | |
|         On generic views OPTIONS should return an 'actions' key with metadata
 | |
|         on the fields that may be supplied to PUT and POST requests.
 | |
|         """
 | |
|         class NestedField(serializers.Serializer):
 | |
|             a = serializers.IntegerField()
 | |
|             b = serializers.IntegerField()
 | |
| 
 | |
|         class ExampleSerializer(serializers.Serializer):
 | |
|             choice_field = serializers.ChoiceField(['red', 'green', 'blue'])
 | |
|             integer_field = serializers.IntegerField(
 | |
|                 min_value=1, max_value=1000
 | |
|             )
 | |
|             char_field = serializers.CharField(
 | |
|                 required=False, min_length=3, max_length=40
 | |
|             )
 | |
|             list_field = serializers.ListField(
 | |
|                 child=serializers.ListField(
 | |
|                     child=serializers.IntegerField()
 | |
|                 )
 | |
|             )
 | |
|             nested_field = NestedField()
 | |
|             uuid_field = serializers.UUIDField(label="UUID field")
 | |
| 
 | |
|         class ExampleView(views.APIView):
 | |
|             """Example view."""
 | |
|             def post(self, request):
 | |
|                 pass
 | |
| 
 | |
|             def get_serializer(self):
 | |
|                 return ExampleSerializer()
 | |
| 
 | |
|         view = ExampleView.as_view()
 | |
|         response = view(request=request)
 | |
|         expected = {
 | |
|             'name': 'Example',
 | |
|             'description': 'Example view.',
 | |
|             'renders': [
 | |
|                 'application/json',
 | |
|                 'text/html'
 | |
|             ],
 | |
|             'parses': [
 | |
|                 'application/json',
 | |
|                 'application/x-www-form-urlencoded',
 | |
|                 'multipart/form-data'
 | |
|             ],
 | |
|             'actions': {
 | |
|                 'POST': {
 | |
|                     'choice_field': {
 | |
|                         'type': 'choice',
 | |
|                         'required': True,
 | |
|                         'read_only': False,
 | |
|                         'label': 'Choice field',
 | |
|                         'choices': [
 | |
|                             {'display_name': 'red', 'value': 'red'},
 | |
|                             {'display_name': 'green', 'value': 'green'},
 | |
|                             {'display_name': 'blue', 'value': 'blue'}
 | |
|                         ]
 | |
|                     },
 | |
|                     'integer_field': {
 | |
|                         'type': 'integer',
 | |
|                         'required': True,
 | |
|                         'read_only': False,
 | |
|                         'label': 'Integer field',
 | |
|                         'min_value': 1,
 | |
|                         'max_value': 1000,
 | |
| 
 | |
|                     },
 | |
|                     'char_field': {
 | |
|                         'type': 'string',
 | |
|                         'required': False,
 | |
|                         'read_only': False,
 | |
|                         'label': 'Char field',
 | |
|                         'min_length': 3,
 | |
|                         'max_length': 40
 | |
|                     },
 | |
|                     'list_field': {
 | |
|                         'type': 'list',
 | |
|                         'required': True,
 | |
|                         'read_only': False,
 | |
|                         'label': 'List field',
 | |
|                         'child': {
 | |
|                             'type': 'list',
 | |
|                             'required': True,
 | |
|                             'read_only': False,
 | |
|                             'child': {
 | |
|                                 'type': 'integer',
 | |
|                                 'required': True,
 | |
|                                 'read_only': False
 | |
|                             }
 | |
|                         }
 | |
|                     },
 | |
|                     'nested_field': {
 | |
|                         'type': 'nested object',
 | |
|                         'required': True,
 | |
|                         'read_only': False,
 | |
|                         'label': 'Nested field',
 | |
|                         'children': {
 | |
|                             'a': {
 | |
|                                 'type': 'integer',
 | |
|                                 'required': True,
 | |
|                                 'read_only': False,
 | |
|                                 'label': 'A'
 | |
|                             },
 | |
|                             'b': {
 | |
|                                 'type': 'integer',
 | |
|                                 'required': True,
 | |
|                                 'read_only': False,
 | |
|                                 'label': 'B'
 | |
|                             }
 | |
|                         }
 | |
|                     },
 | |
|                     'uuid_field': {
 | |
|                         "type": "string",
 | |
|                         "required": True,
 | |
|                         "read_only": False,
 | |
|                         "label": "UUID field",
 | |
|                     },
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|         assert response.status_code == status.HTTP_200_OK
 | |
|         assert response.data == expected
 | |
| 
 | |
|     def test_global_permissions(self):
 | |
|         """
 | |
|         If a user does not have global permissions on an action, then any
 | |
|         metadata associated with it should not be included in OPTION responses.
 | |
|         """
 | |
|         class ExampleSerializer(serializers.Serializer):
 | |
|             choice_field = serializers.ChoiceField(['red', 'green', 'blue'])
 | |
|             integer_field = serializers.IntegerField(max_value=10)
 | |
|             char_field = serializers.CharField(required=False)
 | |
| 
 | |
|         class ExampleView(views.APIView):
 | |
|             """Example view."""
 | |
|             def post(self, request):
 | |
|                 pass
 | |
| 
 | |
|             def put(self, request):
 | |
|                 pass
 | |
| 
 | |
|             def get_serializer(self):
 | |
|                 return ExampleSerializer()
 | |
| 
 | |
|             def check_permissions(self, request):
 | |
|                 if request.method == 'POST':
 | |
|                     raise exceptions.PermissionDenied()
 | |
| 
 | |
|         view = ExampleView.as_view()
 | |
|         response = view(request=request)
 | |
|         assert response.status_code == status.HTTP_200_OK
 | |
|         assert list(response.data['actions']) == ['PUT']
 | |
| 
 | |
|     def test_object_permissions(self):
 | |
|         """
 | |
|         If a user does not have object permissions on an action, then any
 | |
|         metadata associated with it should not be included in OPTION responses.
 | |
|         """
 | |
|         class ExampleSerializer(serializers.Serializer):
 | |
|             choice_field = serializers.ChoiceField(['red', 'green', 'blue'])
 | |
|             integer_field = serializers.IntegerField(max_value=10)
 | |
|             char_field = serializers.CharField(required=False)
 | |
| 
 | |
|         class ExampleView(views.APIView):
 | |
|             """Example view."""
 | |
|             def post(self, request):
 | |
|                 pass
 | |
| 
 | |
|             def put(self, request):
 | |
|                 pass
 | |
| 
 | |
|             def get_serializer(self):
 | |
|                 return ExampleSerializer()
 | |
| 
 | |
|             def get_object(self):
 | |
|                 if self.request.method == 'PUT':
 | |
|                     raise exceptions.PermissionDenied()
 | |
| 
 | |
|         view = ExampleView.as_view()
 | |
|         response = view(request=request)
 | |
|         assert response.status_code == status.HTTP_200_OK
 | |
|         assert list(response.data['actions'].keys()) == ['POST']
 | |
| 
 | |
|     def test_bug_2455_clone_request(self):
 | |
|         class ExampleView(views.APIView):
 | |
|             renderer_classes = (BrowsableAPIRenderer,)
 | |
| 
 | |
|             def post(self, request):
 | |
|                 pass
 | |
| 
 | |
|             def get_serializer(self):
 | |
|                 assert hasattr(self.request, 'version')
 | |
|                 return serializers.Serializer()
 | |
| 
 | |
|         view = ExampleView.as_view()
 | |
|         view(request=request)
 | |
| 
 | |
|     def test_bug_2477_clone_request(self):
 | |
|         class ExampleView(views.APIView):
 | |
|             renderer_classes = (BrowsableAPIRenderer,)
 | |
| 
 | |
|             def post(self, request):
 | |
|                 pass
 | |
| 
 | |
|             def get_serializer(self):
 | |
|                 assert hasattr(self.request, 'versioning_scheme')
 | |
|                 return serializers.Serializer()
 | |
| 
 | |
|         scheme = versioning.QueryParameterVersioning
 | |
|         view = ExampleView.as_view(versioning_class=scheme)
 | |
|         view(request=request)
 | |
| 
 | |
|     def test_dont_show_hidden_fields(self):
 | |
|         """
 | |
|         HiddenField shouldn't show up in SimpleMetadata at all.
 | |
|         """
 | |
|         class ExampleSerializer(serializers.Serializer):
 | |
|             integer_field = serializers.IntegerField(max_value=10)
 | |
|             hidden_field = serializers.HiddenField(default=1)
 | |
| 
 | |
|         class ExampleView(views.APIView):
 | |
|             """Example view."""
 | |
|             def post(self, request):
 | |
|                 pass
 | |
| 
 | |
|             def get_serializer(self):
 | |
|                 return ExampleSerializer()
 | |
| 
 | |
|         view = ExampleView.as_view()
 | |
|         response = view(request=request)
 | |
|         assert response.status_code == status.HTTP_200_OK
 | |
|         assert set(response.data['actions']['POST'].keys()) == {'integer_field'}
 | |
| 
 | |
|     def test_list_serializer_metadata_returns_info_about_fields_of_child_serializer(self):
 | |
|         class ExampleSerializer(serializers.Serializer):
 | |
|             integer_field = serializers.IntegerField(max_value=10)
 | |
|             char_field = serializers.CharField(required=False)
 | |
| 
 | |
|         class ExampleListSerializer(serializers.ListSerializer):
 | |
|             pass
 | |
| 
 | |
|         options = metadata.SimpleMetadata()
 | |
|         child_serializer = ExampleSerializer()
 | |
|         list_serializer = ExampleListSerializer(child=child_serializer)
 | |
|         assert options.get_serializer_info(list_serializer) == options.get_serializer_info(child_serializer)
 | |
| 
 | |
| 
 | |
| class TestSimpleMetadataFieldInfo(TestCase):
 | |
|     def test_null_boolean_field_info_type(self):
 | |
|         options = metadata.SimpleMetadata()
 | |
|         field_info = options.get_field_info(serializers.BooleanField(
 | |
|             allow_null=True))
 | |
|         assert field_info['type'] == 'boolean'
 | |
| 
 | |
|     def test_related_field_choices(self):
 | |
|         options = metadata.SimpleMetadata()
 | |
|         BasicModel.objects.create()
 | |
|         with self.assertNumQueries(0):
 | |
|             field_info = options.get_field_info(
 | |
|                 serializers.RelatedField(queryset=BasicModel.objects.all())
 | |
|             )
 | |
|         assert 'choices' not in field_info
 | |
| 
 | |
|     def test_decimal_field_info_type(self):
 | |
|         options = metadata.SimpleMetadata()
 | |
|         field_info = options.get_field_info(serializers.DecimalField(max_digits=18, decimal_places=4))
 | |
|         assert field_info['type'] == 'decimal'
 | |
|         assert field_info['max_digits'] == 18
 | |
|         assert field_info['decimal_places'] == 4
 | |
| 
 | |
| 
 | |
| class TestModelSerializerMetadata(TestCase):
 | |
|     def test_read_only_primary_key_related_field(self):
 | |
|         """
 | |
|         On generic views OPTIONS should return an 'actions' key with metadata
 | |
|         on the fields that may be supplied to PUT and POST requests. It should
 | |
|         not fail when a read_only PrimaryKeyRelatedField is present
 | |
|         """
 | |
|         class Parent(models.Model):
 | |
|             integer_field = models.IntegerField(validators=[MinValueValidator(1), MaxValueValidator(1000)])
 | |
|             children = models.ManyToManyField('Child')
 | |
|             name = models.CharField(max_length=100, blank=True, null=True)
 | |
| 
 | |
|         class Child(models.Model):
 | |
|             name = models.CharField(max_length=100)
 | |
| 
 | |
|         class ExampleSerializer(serializers.ModelSerializer):
 | |
|             children = serializers.PrimaryKeyRelatedField(read_only=True, many=True)
 | |
| 
 | |
|             class Meta:
 | |
|                 model = Parent
 | |
|                 fields = '__all__'
 | |
| 
 | |
|         class ExampleView(views.APIView):
 | |
|             """Example view."""
 | |
|             def post(self, request):
 | |
|                 pass
 | |
| 
 | |
|             def get_serializer(self):
 | |
|                 return ExampleSerializer()
 | |
| 
 | |
|         view = ExampleView.as_view()
 | |
|         response = view(request=request)
 | |
|         expected = {
 | |
|             'name': 'Example',
 | |
|             'description': 'Example view.',
 | |
|             'renders': [
 | |
|                 'application/json',
 | |
|                 'text/html'
 | |
|             ],
 | |
|             'parses': [
 | |
|                 'application/json',
 | |
|                 'application/x-www-form-urlencoded',
 | |
|                 'multipart/form-data'
 | |
|             ],
 | |
|             'actions': {
 | |
|                 'POST': {
 | |
|                     'id': {
 | |
|                         'type': 'integer',
 | |
|                         'required': False,
 | |
|                         'read_only': True,
 | |
|                         'label': 'ID'
 | |
|                     },
 | |
|                     'children': {
 | |
|                         'type': 'field',
 | |
|                         'required': False,
 | |
|                         'read_only': True,
 | |
|                         'label': 'Children'
 | |
|                     },
 | |
|                     'integer_field': {
 | |
|                         'type': 'integer',
 | |
|                         'required': True,
 | |
|                         'read_only': False,
 | |
|                         'label': 'Integer field',
 | |
|                         'min_value': 1,
 | |
|                         'max_value': 1000
 | |
|                     },
 | |
|                     'name': {
 | |
|                         'type': 'string',
 | |
|                         'required': False,
 | |
|                         'read_only': False,
 | |
|                         'label': 'Name',
 | |
|                         'max_length': 100
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         assert response.status_code == status.HTTP_200_OK
 | |
|         assert response.data == expected
 |