diff --git a/docs/api-guide/schemas.md b/docs/api-guide/schemas.md index d48bafdab3e..5fc335ea68c 100644 --- a/docs/api-guide/schemas.md +++ b/docs/api-guide/schemas.md @@ -164,8 +164,7 @@ appropriate Core API `Link` object for the view, request method and path: auto_schema = view.schema coreapi_link = auto_schema.get_link(...) - -(Aside: In compiling the schema, `SchemaGenerator` calls `view.schema.get_link()` for +(In compiling the schema, `SchemaGenerator` calls `view.schema.get_link()` for each view, allowed method and path.) To customise the `Link` generation you may: @@ -178,9 +177,9 @@ To customise the `Link` generation you may: class CustomView(APIView): ... schema = AutoSchema( - manual_fields= { - "extra_field": coreapi.Field(...) - } + manual_fields=[ + coreapi.Field("extra_field", ...), + ] ) This allows extension for the most common case without subclassing. @@ -512,9 +511,22 @@ A class that deals with introspection of individual views for schema generation. `AutoSchema` is attached to `APIView` via the `schema` attribute. -Typically you will subclass `AutoSchema` to customise schema generation -and then set your subclass on your view. +The `AutoSchema` constructor takes a single keyword argument `manual_fields`. + +**`manual_fields`**: a `list` of `coreapi.Field` instances that will be added to +the generated fields. Generated fields with a matching `name` will be overwritten. + class CustomView(APIView): + schema = AutoSchema(manual_fields=[ + coreapi.Field( + "my_extra_field", + required=True, + location="path", + schema=coreschema.String() + ), + ]) + +For more advanced customisation subclass `AutoSchema` to customise schema generation. class CustomViewSchema(AutoSchema): """ @@ -529,10 +541,13 @@ and then set your subclass on your view. class MyView(APIView): schema = CustomViewSchema() +The following methods are available to override. + ### get_link(self, path, method, base_url) Returns a `coreapi.Link` instance corresponding to the given view. +This is the main entry point. You can override this if you need to provide custom behaviors for particular views. ### get_description(self, path, method) @@ -565,7 +580,7 @@ Return a list of `coreapi.Link()` instances, as returned by the `get_schema_fiel ## ManualSchema -`APIViewSchemaDescriptor` subclass for specifying a manual schema. +Allows specifying a manual schema for a view: class MyView(APIView): schema = ManualSchema(coreapi.Link( diff --git a/rest_framework/schemas.py b/rest_framework/schemas.py index 7ca8193709d..29989e6cdcc 100644 --- a/rest_framework/schemas.py +++ b/rest_framework/schemas.py @@ -301,12 +301,28 @@ class AutoSchema(ViewInspector): Responsible for per-view instrospection and schema generation. """ + def __init__(self, manual_fields=None): + """ + Parameters: + + * `manual_fields`: list of `coreapi.Field` instances that + will be added to auto-generated fields, overwriting on `Field.name` + """ + + self._manual_fields = manual_fields + def get_link(self, path, method, base_url): fields = self.get_path_fields(path, method) fields += self.get_serializer_fields(path, method) fields += self.get_pagination_fields(path, method) fields += self.get_filter_fields(path, method) + if self._manual_fields is not None: + by_name = {f.name: f for f in fields} + for f in self._manual_fields: + by_name[f.name] = f + fields = list(by_name.values()) + if fields and any([field.location in ('form', 'body') for field in fields]): encoding = self.get_encoding(path, method) else: diff --git a/tests/test_schemas.py b/tests/test_schemas.py index 1b17514cbc9..28fab41c2e7 100644 --- a/tests/test_schemas.py +++ b/tests/test_schemas.py @@ -513,6 +513,25 @@ def test_get_link_requires_instance(self): with pytest.raises(AssertionError): descriptor.get_link(None, None, None) # ???: Do the dummy arguments require a tighter assert? + def test_manual_fields(self): + + class CustomView(APIView): + schema = AutoSchema(manual_fields=[ + coreapi.Field( + "my_extra_field", + required=True, + location="path", + schema=coreschema.String() + ), + ]) + + view = CustomView() + link = view.schema.get_link('/a/url/{id}/', 'GET', '') + fields = link.fields + + assert len(fields) == 2 + assert "my_extra_field" in [f.name for f in fields] + def test_view_with_manual_schema(self): expected = coreapi.Link(