Repository: encode/django-rest-framework

Issue:
DRF generates incorrect OpenAPI schema for generic views' delete method 
## Checklist

- [x] I have verified that that issue exists against the `master` branch of Django REST framework.
- [x] I have searched for similar issues in both open and closed tickets and cannot find a duplicate.
- [x] This is not a usage question. (Those should be directed to the [discussion group](https://groups.google.com/forum/#!forum/django-rest-framework) instead.)
- [x] This cannot be dealt with as a third party library. (We prefer new functionality to be [in the form of third party libraries](https://www.django-rest-framework.org/community/third-party-packages/#about-third-party-packages) where possible.)
- [x] I have reduced the issue to the simplest possible case.
- [ ] I have included a failing test as a pull request. (If you are unable to do so we can still accept the issue.)

## Steps to reproduce
```
# view.py
from rest_framework import generics
from rest_framework import serializers
from django.db import models

class Book(models.Model):
  title = models.CharField(max_length=200)

class BookSerializer(serializers.ModelSerializer):
  class Meta:
    model = Book
    fields = '__all__'

class BookDetail(generics.RetrieveUpdateDestroyAPIView):
  queryset = Book.objects.all()   
  serializer_class = BookSerializer

# urls.py
urlpatterns = [
...
path('api/books/<int:pk>/', views.BookDetail.as_view()),
...
]
```

## Expected behavior
```
$ ./manage.py generateschema

openapi: 3.0.2
info:
  title: ''
  version: TODO
paths:
  /api/books/{id}/:
    delete:
      operationId: DestroyBook
      parameters:
      - name: id
        in: path
        required: true
        description: A unique integer value identifying this book.
        schema:
          type: string
      responses:
        '204':
          description: ''
```

## Actual behavior
```
$ ./manage.py generateschema

openapi: 3.0.2
info:
  title: ''
  version: TODO
paths:
  /api/books/{id}/:
    delete:
      operationId: DestroyBook
      parameters:
      - name: id
        in: path
        required: true
        description: A unique integer value identifying this book.
        schema:
          type: string
      responses:
        '200':
          content:
            application/json:
              schema:
                required:
                - title
                properties:
                  id:
                    type: integer
                    readOnly: true
                  title:
                    type: string
                    maxLength: 200
```
DRF generates incorrect OpenAPI schema for generic views' delete method 
## Checklist

- [x] I have verified that that issue exists against the `master` branch of Django REST framework.
- [x] I have searched for similar issues in both open and closed tickets and cannot find a duplicate.
- [x] This is not a usage question. (Those should be directed to the [discussion group](https://groups.google.com/forum/#!forum/django-rest-framework) instead.)
- [x] This cannot be dealt with as a third party library. (We prefer new functionality to be [in the form of third party libraries](https://www.django-rest-framework.org/community/third-party-packages/#about-third-party-packages) where possible.)
- [x] I have reduced the issue to the simplest possible case.
- [ ] I have included a failing test as a pull request. (If you are unable to do so we can still accept the issue.)

## Steps to reproduce
```
# view.py
from rest_framework import generics
from rest_framework import serializers
from django.db import models

class Book(models.Model):
  title = models.CharField(max_length=200)

class BookSerializer(serializers.ModelSerializer):
  class Meta:
    model = Book
    fields = '__all__'

class BookDetail(generics.RetrieveUpdateDestroyAPIView):
  queryset = Book.objects.all()   
  serializer_class = BookSerializer

# urls.py
urlpatterns = [
...
path('api/books/<int:pk>/', views.BookDetail.as_view()),
...
]
```

## Expected behavior
```
$ ./manage.py generateschema

openapi: 3.0.2
info:
  title: ''
  version: TODO
paths:
  /api/books/{id}/:
    delete:
      operationId: DestroyBook
      parameters:
      - name: id
        in: path
        required: true
        description: A unique integer value identifying this book.
        schema:
          type: string
      responses:
        '204':
          description: ''
```

## Actual behavior
```
$ ./manage.py generateschema

openapi: 3.0.2
info:
  title: ''
  version: TODO
paths:
  /api/books/{id}/:
    delete:
      operationId: DestroyBook
      parameters:
      - name: id
        in: path
        required: true
        description: A unique integer value identifying this book.
        schema:
          type: string
      responses:
        '200':
          content:
            application/json:
              schema:
                required:
                - title
                properties:
                  id:
                    type: integer
                    readOnly: true
                  title:
                    type: string
                    maxLength: 200
```


Discussion:
Yes, this is valid. `DestroyModelMixin` returns `Response(status=status.HTTP_204_NO_CONTENT)`.

Fix here should be a test case plus an adjustment to _get_responses to sensibly handle the `DELETE` method case. 

https://github.com/encode/django-rest-framework/blob/b45ff072947d01917132ef6993bf1b7f46d9f2c8/rest_framework/schemas/openapi.py#L462

Maybe could be as simple as

```
# as a guard at the beginning...
if method == 'DELETE':
    return {'204': {description: ''}}
```

Thanks for the report @knivets. Fancy working on a PR for this? 
Sure I can work on this. Will submit a PR this week.
Awesome. Welcome aboard! 🎉

If you need any assistance open a PR early and @carltongibson me, so I'll get a message. 
Thanks Carlton! :)
Yes, this is valid. `DestroyModelMixin` returns `Response(status=status.HTTP_204_NO_CONTENT)`.

Fix here should be a test case plus an adjustment to _get_responses to sensibly handle the `DELETE` method case. 

https://github.com/encode/django-rest-framework/blob/b45ff072947d01917132ef6993bf1b7f46d9f2c8/rest_framework/schemas/openapi.py#L462

Maybe could be as simple as

```
# as a guard at the beginning...
if method == 'DELETE':
    return {'204': {description: ''}}
```

Thanks for the report @knivets. Fancy working on a PR for this? 
Sure I can work on this. Will submit a PR this week.
Awesome. Welcome aboard! 🎉

If you need any assistance open a PR early and @carltongibson me, so I'll get a message. 
Thanks Carlton! :)

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
@@ -465,6 +465,13 @@ def _get_request_body(self, path, method):
 
     def _get_responses(self, path, method):
         # TODO: Handle multiple codes and pagination classes.
+        if method == 'DELETE':
+            return {
+                '204': {
+                    'description': ''
+                }
+            }
+
         item_schema = {}
         serializer = self._get_serializer(path, method)
 


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
@@ -264,6 +264,29 @@ class View(generics.GenericAPIView):
             },
         }
 
+    def test_delete_response_body_generation(self):
+        """Test that a view's delete method generates a proper response body schema."""
+        path = '/{id}/'
+        method = 'DELETE'
+
+        class View(generics.DestroyAPIView):
+            serializer_class = views.ExampleSerializer
+
+        view = create_view(
+            View,
+            method,
+            create_request(path),
+        )
+        inspector = AutoSchema()
+        inspector.view = view
+
+        responses = inspector._get_responses(path, method)
+        assert responses == {
+            '204': {
+                'description': '',
+            },
+        }
+
     def test_retrieve_response_body_generation(self):
         """Test that a list of properties is returned for retrieve item views."""
         path = '/{id}/'