Repository: encode/django-rest-framework

Issue:
OpenAPI: Schema.required must not be an empty list
Note: I don't know where (or whether) the [spec](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#schema-object) actually says so, but editor.swagger.io complains about empty `Schema.required` properties. 

`AutoSchema._get_responses` and `AutoSchema._get_request_body` happily create such empty lists and therefore it should be fixed there.


Discussion:
I assume it's just following the spec badly there, but we should resolve it anyways.
Presumably we should just pop `required` out if it's an empty list?
Want to take a crack at a pull request for this?
Assign both tickets to me, I'll have a go at this tomorrow, today it's just too hot for work ;)
Hey @chrisv2. Super thanks. (Again if you want/need input open the PR early and I'm happy to comment.) Champion. 
I'll roll another minor release once this one is in.

(Worth ironing out the various niggles here quickly.)

Resolving patch:
diff --git a/rest_framework/schemas/openapi.py b/rest_framework/schemas/openapi.py
--- a/rest_framework/schemas/openapi.py
+++ b/rest_framework/schemas/openapi.py
@@ -381,10 +381,14 @@ def _map_serializer(self, serializer):
             self._map_field_validators(field.validators, schema)
 
             properties[field.field_name] = schema
-        return {
-            'required': required,
-            'properties': properties,
+
+        result = {
+            'properties': properties
         }
+        if len(required) > 0:
+            result['required'] = required
+
+        return result
 
     def _map_field_validators(self, validators, schema):
         """
@@ -470,7 +474,8 @@ def _get_responses(self, path, method):
             for name, schema in item_schema['properties'].copy().items():
                 if 'writeOnly' in schema:
                     del item_schema['properties'][name]
-                    item_schema['required'] = [f for f in item_schema['required'] if f != name]
+                    if 'required' in item_schema:
+                        item_schema['required'] = [f for f in item_schema['required'] if f != name]
 
         if is_list_view(path, method, self.view):
             response_schema = {


Test patch:
diff --git a/tests/schemas/test_openapi.py b/tests/schemas/test_openapi.py
--- a/tests/schemas/test_openapi.py
+++ b/tests/schemas/test_openapi.py
@@ -142,6 +142,32 @@ class View(generics.GenericAPIView):
         assert request_body['content']['application/json']['schema']['required'] == ['text']
         assert list(request_body['content']['application/json']['schema']['properties'].keys()) == ['text']
 
+    def test_empty_required(self):
+        path = '/'
+        method = 'POST'
+
+        class Serializer(serializers.Serializer):
+            read_only = serializers.CharField(read_only=True)
+            write_only = serializers.CharField(write_only=True, required=False)
+
+        class View(generics.GenericAPIView):
+            serializer_class = Serializer
+
+        view = create_view(
+            View,
+            method,
+            create_request(path)
+        )
+        inspector = AutoSchema()
+        inspector.view = view
+
+        request_body = inspector._get_request_body(path, method)
+        # there should be no empty 'required' property, see #6834
+        assert 'required' not in request_body['content']['application/json']['schema']
+
+        for response in inspector._get_responses(path, method).values():
+            assert 'required' not in response['content']['application/json']['schema']
+
     def test_response_body_generation(self):
         path = '/'
         method = 'POST'