Repository: encode/django-rest-framework

Issue:
OpenAPI AutoSchema does not handle ListField correctly
## 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
djangorestframework==3.10.0

```python
# serializers.py
class MySerializer(serializers.Serializer):
    my_values = serializers.ListField(child=serializers.FloatField())

# views.py
class MyAPIView(GenericAPIView):
    serializer_class = MySerializer
```
`urls.py` and `templates/swagger-ui.html` are same as described in [documentation](https://www.django-rest-framework.org/topics/documenting-your-api/).

## Expected behavior
Swagger should render schema without any problems.

## Actual behavior
swagger
![swagger](https://i.imgur.com/t4Eaylp.png)
console
![console](https://i.imgur.com/P0BlLAF.png)

I think the latest version of drf does not handle `ListField` correctly. The `array` type in OpenAPI should come with corresponding `items`, but `AutoSchema` in `rest_framework.schemas.openapi` does not provide `items` for `ListField` in `_map_field` method.
https://github.com/encode/django-rest-framework/blob/master/rest_framework/schemas/openapi.py#L257-L261


Discussion:
OK, looks like a good addition. Do you think you could make a PR adding the correct mapping for ListField?

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
@@ -9,7 +9,7 @@
 
 from rest_framework import exceptions, serializers
 from rest_framework.compat import uritemplate
-from rest_framework.fields import empty
+from rest_framework.fields import _UnvalidatedField, empty
 
 from .generators import BaseSchemaGenerator
 from .inspectors import ViewInspector
@@ -256,9 +256,15 @@ def _map_field(self, field):
 
         # ListField.
         if isinstance(field, serializers.ListField):
-            return {
+            mapping = {
                 'type': 'array',
+                'items': {},
             }
+            if not isinstance(field.child, _UnvalidatedField):
+                mapping['items'] = {
+                    "type": self._map_field(field.child).get('type')
+                }
+            return mapping
 
         # DateField and DateTimeField type is string
         if isinstance(field, serializers.DateField):


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
@@ -39,6 +39,20 @@ def test_pagination(self):
             assert f.get_schema_operation_parameters(self.dummy_view)
 
 
+class TestFieldMapping(TestCase):
+    def test_list_field_mapping(self):
+        inspector = AutoSchema()
+        cases = [
+            (serializers.ListField(), {'items': {}, 'type': 'array'}),
+            (serializers.ListField(child=serializers.BooleanField()), {'items': {'type': 'boolean'}, 'type': 'array'}),
+            (serializers.ListField(child=serializers.FloatField()), {'items': {'type': 'number'}, 'type': 'array'}),
+            (serializers.ListField(child=serializers.CharField()), {'items': {'type': 'string'}, 'type': 'array'}),
+        ]
+        for field, mapping in cases:
+            with self.subTest(field=field):
+                assert inspector._map_field(field) == mapping
+
+
 @pytest.mark.skipif(uritemplate is None, reason='uritemplate not installed.')
 class TestOperationIntrospection(TestCase):