From 728fdbe13c7ac859727c2ab106d3cb20cf8444c0 Mon Sep 17 00:00:00 2001 From: Rizwan Shaikh Date: Tue, 27 Jun 2023 22:55:52 +0530 Subject: [PATCH] updated field default on serializer according to openapi generation and added that to options action response --- rest_framework/metadata.py | 4 + rest_framework/utils/field_mapping.py | 3 +- tests/test_metadata.py | 129 ++++++++++++++++++++++++++ 3 files changed, 135 insertions(+), 1 deletion(-) diff --git a/rest_framework/metadata.py b/rest_framework/metadata.py index 364ca5b14..fd0f4e163 100644 --- a/rest_framework/metadata.py +++ b/rest_framework/metadata.py @@ -11,6 +11,7 @@ from django.http import Http404 from django.utils.encoding import force_str from rest_framework import exceptions, serializers +from rest_framework.fields import empty from rest_framework.request import clone_request from rest_framework.utils.field_mapping import ClassLookupDict @@ -149,4 +150,7 @@ class SimpleMetadata(BaseMetadata): for choice_value, choice_name in field.choices.items() ] + if getattr(field, 'default', None) and field.default != empty and not callable(field.default): + field_info['default'] = field.default + return field_info diff --git a/rest_framework/utils/field_mapping.py b/rest_framework/utils/field_mapping.py index fbb048628..30bb65e0c 100644 --- a/rest_framework/utils/field_mapping.py +++ b/rest_framework/utils/field_mapping.py @@ -9,6 +9,7 @@ from django.db import models from django.utils.text import capfirst from rest_framework.compat import postgres_fields +from rest_framework.fields import empty from rest_framework.validators import UniqueValidator NUMERIC_FIELD_TYPES = ( @@ -127,7 +128,7 @@ def get_field_kwargs(field_name, model_field): kwargs['read_only'] = True return kwargs - if model_field.has_default(): + if model_field.default is not None and model_field.default != empty and not callable(model_field.default): kwargs['default'] = model_field.default if model_field.has_default() or model_field.blank or model_field.null: diff --git a/tests/test_metadata.py b/tests/test_metadata.py index 1bdc8697c..387b9ecde 100644 --- a/tests/test_metadata.py +++ b/tests/test_metadata.py @@ -184,6 +184,135 @@ class TestMetadata: assert response.status_code == status.HTTP_200_OK assert response.data == expected + def test_actions_with_default(self): + """ + On generic views OPTIONS should return an 'actions' key with metadata + on the fields with default that may be supplied to PUT and POST requests. + """ + class NestedField(serializers.Serializer): + a = serializers.IntegerField(default=2) + b = serializers.IntegerField() + + class ExampleSerializer(serializers.Serializer): + choice_field = serializers.ChoiceField(['red', 'green', 'blue'], default='red') + integer_field = serializers.IntegerField( + min_value=1, max_value=1000, default=1 + ) + char_field = serializers.CharField( + min_length=3, max_length=40, default="example" + ) + list_field = serializers.ListField( + child=serializers.ListField( + child=serializers.IntegerField(default=1) + ) + ) + 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': False, + 'read_only': False, + 'label': 'Choice field', + "choices": [ + {'value': 'red', 'display_name': 'red'}, + {'value': 'green', 'display_name': 'green'}, + {'value': 'blue', 'display_name': 'blue'} + ], + 'default': 'red' + }, + 'integer_field': { + 'type': 'integer', + 'required': False, + 'read_only': False, + 'label': 'Integer field', + 'min_value': 1, + 'max_value': 1000, + 'default': 1 + }, + 'char_field': { + 'type': 'string', + 'required': False, + 'read_only': False, + 'label': 'Char field', + 'min_length': 3, + 'max_length': 40, + 'default': 'example' + }, + 'list_field': { + 'type': 'list', + 'required': True, + 'read_only': False, + 'label': 'List field', + 'child': { + 'type': 'list', + 'required': True, + 'read_only': False, + 'child': { + 'type': 'integer', + 'required': False, + 'read_only': False, + 'default': 1 + } + } + }, + 'nested_field': { + 'type': 'nested object', + 'required': True, + 'read_only': False, + 'label': 'Nested field', + 'children': { + 'a': { + 'type': 'integer', + 'required': False, + 'read_only': False, + 'label': 'A', + 'default': 2 + }, + '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