File: schemas.md

package info (click to toggle)
djangorestframework 3.16.1-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 19,092 kB
  • sloc: javascript: 31,965; python: 29,367; makefile: 35; sh: 6
file content (465 lines) | stat: -rw-r--r-- 16,594 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
---
source:
    - schemas
---

# Schema

> A machine-readable [schema] describes what resources are available via the API, what their URLs are, how they are represented and what operations they support.
>
> — Heroku, [JSON Schema for the Heroku Platform API][cite]

---

**Deprecation notice:**

REST framework's built-in support for generating OpenAPI schemas is
**deprecated** in favor of 3rd party packages that can provide this
functionality instead. The built-in support will be moved into a separate
package and then subsequently retired over the next releases.

As a full-fledged replacement, we recommend the [drf-spectacular] package.
It has extensive support for generating OpenAPI 3 schemas from
REST framework APIs, with both automatic and customisable options available.
For further information please refer to
[Documenting your API](../topics/documenting-your-api.md#drf-spectacular).

---

API schemas are a useful tool that allow for a range of use cases, including
generating reference documentation, or driving dynamic client libraries that
can interact with your API.

Django REST Framework provides support for automatic generation of
[OpenAPI][openapi] schemas.

## Overview

Schema generation has several moving parts. It's worth having an overview:

* `SchemaGenerator` is a top-level class that is responsible for walking your
  configured URL patterns, finding `APIView` subclasses, enquiring for their
  schema representation, and compiling the final schema object.
* `AutoSchema` encapsulates all the details necessary for per-view schema
  introspection. Is attached to each view via the `schema` attribute. You
  subclass `AutoSchema` in order to customize your schema.
* The `generateschema` management command allows you to generate a static schema
  offline.
* Alternatively, you can route `SchemaView` to dynamically generate and serve
  your schema.
* `settings.DEFAULT_SCHEMA_CLASS` allows you to specify an `AutoSchema`
  subclass to serve as your project's default.

The following sections explain more.

## Generating an OpenAPI Schema

### Install dependencies

    pip install pyyaml uritemplate inflection

* `pyyaml` is used to generate schema into YAML-based OpenAPI format.
* `uritemplate` is used internally to get parameters in path.
* `inflection` is used to pluralize operations more appropriately in the list endpoints.

### Generating a static schema with the `generateschema` management command

If your schema is static, you can use the `generateschema` management command:

```bash
./manage.py generateschema --file openapi-schema.yml
```

Once you've generated a schema in this way you can annotate it with any
additional information that cannot be automatically inferred by the schema
generator.

You might want to check your API schema into version control and update it
with each new release, or serve the API schema from your site's static media.

### Generating a dynamic schema with `SchemaView`

If you require a dynamic schema, because foreign key choices depend on database
values, for example, you can route a `SchemaView` that will generate and serve
your schema on demand.

To route a `SchemaView`, use the `get_schema_view()` helper.

In `urls.py`:

```python
from rest_framework.schemas import get_schema_view

urlpatterns = [
    # ...
    # Use the `get_schema_view()` helper to add a `SchemaView` to project URLs.
    #   * `title` and `description` parameters are passed to `SchemaGenerator`.
    #   * Provide view name for use with `reverse()`.
    path(
        "openapi",
        get_schema_view(
            title="Your Project", description="API for all things …", version="1.0.0"
        ),
        name="openapi-schema",
    ),
    # ...
]
```

#### `get_schema_view()`

The `get_schema_view()` helper takes the following keyword arguments:

* `title`: May be used to provide a descriptive title for the schema definition.
* `description`: Longer descriptive text.
* `version`: The version of the API.
* `url`: May be used to pass a canonical base URL for the schema.

        schema_view = get_schema_view(
            title='Server Monitoring API',
            url='https://www.example.org/api/'
        )

* `urlconf`: A string representing the import path to the URL conf that you want
   to generate an API schema for. This defaults to the value of Django's
   `ROOT_URLCONF` setting.

        schema_view = get_schema_view(
            title='Server Monitoring API',
            url='https://www.example.org/api/',
            urlconf='myproject.urls'
        )

* `patterns`: List of url patterns to limit the schema introspection to. If you
  only want the `myproject.api` urls to be exposed in the schema:

        schema_url_patterns = [
            path('api/', include('myproject.api.urls')),
        ]

        schema_view = get_schema_view(
            title='Server Monitoring API',
            url='https://www.example.org/api/',
            patterns=schema_url_patterns,
        )
* `public`: May be used to specify if schema should bypass views permissions. Default to False

* `generator_class`: May be used to specify a `SchemaGenerator` subclass to be
  passed to the `SchemaView`.
* `authentication_classes`: May be used to specify the list of authentication
  classes that will apply to the schema endpoint. Defaults to
  `settings.DEFAULT_AUTHENTICATION_CLASSES`
* `permission_classes`: May be used to specify the list of permission classes
  that will apply to the schema endpoint. Defaults to
  `settings.DEFAULT_PERMISSION_CLASSES`.
* `renderer_classes`: May be used to pass the set of renderer classes that can
  be used to render the API root endpoint.


## SchemaGenerator

**Schema-level customization**

```python
from rest_framework.schemas.openapi import SchemaGenerator
```

`SchemaGenerator` is a class that walks a list of routed URL patterns, requests
the schema for each view and collates the resulting OpenAPI schema.

Typically you won't need to instantiate `SchemaGenerator` yourself, but you can
do so like so:

    generator = SchemaGenerator(title='Stock Prices API')

Arguments:

* `title` **required**: The name of the API.
* `description`: Longer descriptive text.
* `version`: The version of the API. Defaults to `0.1.0`.
* `url`: The root URL of the API schema. This option is not required unless the schema is included under path prefix.
* `patterns`: A list of URLs to inspect when generating the schema. Defaults to the project's URL conf.
* `urlconf`: A URL conf module name to use when generating the schema. Defaults to `settings.ROOT_URLCONF`.

In order to customize the top-level schema, subclass
`rest_framework.schemas.openapi.SchemaGenerator` and provide your subclass
as an argument to the `generateschema` command or `get_schema_view()` helper
function.

### get_schema(self, request=None, public=False)

Returns a dictionary that represents the OpenAPI schema:

    generator = SchemaGenerator(title='Stock Prices API')
    schema = generator.get_schema()

The `request` argument is optional, and may be used if you want to apply
per-user permissions to the resulting schema generation.

This is a good point to override if you want to customize the generated
dictionary For example you might wish to add terms of service to the [top-level
`info` object][info-object]:

```
class TOSSchemaGenerator(SchemaGenerator):
    def get_schema(self, *args, **kwargs):
        schema = super().get_schema(*args, **kwargs)
        schema["info"]["termsOfService"] = "https://example.com/tos.html"
        return schema
```

## AutoSchema

**Per-View Customization**

```python
from rest_framework.schemas.openapi import AutoSchema
```

By default, view introspection is performed by an `AutoSchema` instance
accessible via the `schema` attribute on `APIView`.

    auto_schema = some_view.schema

`AutoSchema` provides the OpenAPI elements needed for each view, request method
and path:

* A list of [OpenAPI components][openapi-components]. In DRF terms these are
  mappings of serializers that describe request and response bodies.
* The appropriate [OpenAPI operation object][openapi-operation] that describes
  the endpoint, including path and query parameters for pagination, filtering,
  and so on.

```python
components = auto_schema.get_components(...)
operation = auto_schema.get_operation(...)
```

In compiling the schema, `SchemaGenerator` calls `get_components()` and
`get_operation()` for each view, allowed method, and path.

----

**Note**: The automatic introspection of components, and many operation
parameters relies on the relevant attributes and methods of
`GenericAPIView`: `get_serializer()`, `pagination_class`, `filter_backends`,
etc. For basic `APIView` subclasses, default introspection is essentially limited to
the URL kwarg path parameters for this reason.

----

`AutoSchema` encapsulates the view introspection needed for schema generation.
Because of this all the schema generation logic is kept in a single place,
rather than being spread around the already extensive view, serializer and
field APIs.

Keeping with this pattern, try not to let schema logic leak into your own
views, serializers, or fields when customizing the schema generation. You might
be tempted to do something like this:

```python
class CustomSchema(AutoSchema):
    """
    AutoSchema subclass using schema_extra_info on the view.
    """

    ...


class CustomView(APIView):
    schema = CustomSchema()
    schema_extra_info = ...  # some extra info
```

Here, the `AutoSchema` subclass goes looking for `schema_extra_info` on the
view. This is _OK_ (it doesn't actually hurt) but it means you'll end up with
your schema logic spread out in a number of different places.

Instead try to subclass `AutoSchema` such that the `extra_info` doesn't leak
out into the view:

```python
class BaseSchema(AutoSchema):
    """
    AutoSchema subclass that knows how to use extra_info.
    """

    ...


class CustomSchema(BaseSchema):
    extra_info = ...  # some extra info


class CustomView(APIView):
    schema = CustomSchema()
```

This style is slightly more verbose but maintains the encapsulation of the
schema related code. It's more _cohesive_ in the _parlance_. It'll keep the
rest of your API code more tidy.

If an option applies to many view classes, rather than creating a specific
subclass per-view, you may find it more convenient to allow specifying the
option as an `__init__()` kwarg to your base `AutoSchema` subclass:

```python
class CustomSchema(BaseSchema):
    def __init__(self, **kwargs):
        # store extra_info for later
        self.extra_info = kwargs.pop("extra_info")
        super().__init__(**kwargs)


class CustomView(APIView):
    schema = CustomSchema(extra_info=...)  # some extra info
```

This saves you having to create a custom subclass per-view for a commonly used option.

Not all `AutoSchema` methods expose related `__init__()` kwargs, but those for
the more commonly needed options do.

### `AutoSchema` methods

#### `get_components()`

Generates the OpenAPI components that describe request and response bodies,
deriving their properties from the serializer.

Returns a dictionary mapping the component name to the generated
representation. By default this has just a single pair but you may override
`get_components()` to return multiple pairs if your view uses multiple
serializers.

#### `get_component_name()`

Computes the component's name from the serializer.

You may see warnings if your API has duplicate component names. If so you can override `get_component_name()` or pass the `component_name` `__init__()` kwarg (see below) to provide different names.

#### `get_reference()`

Returns a reference to the serializer component. This may be useful if you override `get_schema()`.


#### `map_serializer()`

Maps serializers to their OpenAPI representations.

Most serializers should conform to the standard OpenAPI `object` type, but you may
wish to override `map_serializer()` in order to customize this or other
serializer-level fields.

#### `map_field()`

Maps individual serializer fields to their schema representation. The base implementation
will handle the default fields that Django REST Framework provides.

For `SerializerMethodField` instances, for which the schema is unknown, or custom field subclasses you should override `map_field()` to generate the correct schema:

```python
class CustomSchema(AutoSchema):
    """Extension of ``AutoSchema`` to add support for custom field schemas."""

    def map_field(self, field):
        # Handle SerializerMethodFields or custom fields here...
        # ...
        return super().map_field(field)
```

Authors of third-party packages should aim to provide an `AutoSchema` subclass,
and a mixin, overriding `map_field()` so that users can easily generate schemas
for their custom fields.

#### `get_tags()`

OpenAPI groups operations by tags. By default tags taken from the first path
segment of the routed URL. For example, a URL like `/users/{id}/` will generate
the tag `users`.

You can pass an `__init__()` kwarg to manually specify tags (see below), or
override `get_tags()` to provide custom logic.

#### `get_operation()`

Returns the [OpenAPI operation object][openapi-operation] that describes the
endpoint, including path and query parameters for pagination, filtering, and so
on.

Together with `get_components()`, this is the main entry point to the view
introspection.

#### `get_operation_id()`

There must be a unique [operationid](openapi-operationid) for each operation.
By default the `operationId` is deduced from the model name, serializer name or
view name. The operationId looks like "listItems", "retrieveItem",
"updateItem", etc. The `operationId` is camelCase by convention.

#### `get_operation_id_base()`

If you have several views with the same model name, you may see duplicate
operationIds.

In order to work around this, you can override `get_operation_id_base()` to
provide a different base for name part of the ID.

#### `get_serializer()`

If the view has implemented `get_serializer()`, returns the result.

#### `get_request_serializer()`

By default returns `get_serializer()` but can be overridden to
differentiate between request and response objects.

#### `get_response_serializer()`

By default returns `get_serializer()` but can be overridden to
differentiate between request and response objects.

### `AutoSchema.__init__()` kwargs

`AutoSchema` provides a number of `__init__()` kwargs that can be used for
common customizations, if the default generated values are not appropriate.

The available kwargs are:

* `tags`: Specify a list of tags.
* `component_name`: Specify the component name.
* `operation_id_base`: Specify the resource-name part of operation IDs.

You pass the kwargs when declaring the `AutoSchema` instance on your view:

```
class PetDetailView(generics.RetrieveUpdateDestroyAPIView):
    schema = AutoSchema(
        tags=['Pets'],
        component_name='Pet',
        operation_id_base='Pet',
    )
    ...
```

Assuming a `Pet` model and `PetSerializer` serializer, the kwargs in this
example are probably not needed. Often, though, you'll need to pass the kwargs
if you have multiple view targeting the same model, or have multiple views with
identically named serializers.

If your views have related customizations that are needed frequently, you can
create a base `AutoSchema` subclass for your project that takes additional
`__init__()` kwargs to save subclassing `AutoSchema` for each view.

[cite]: https://www.heroku.com/blog/json_schema_for_heroku_platform_api/
[openapi]: https://github.com/OAI/OpenAPI-Specification
[openapi-specification-extensions]: https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#specification-extensions
[openapi-operation]: https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#operationObject
[openapi-tags]: https://swagger.io/specification/#tagObject
[openapi-operationid]: https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#fixed-fields-17
[openapi-components]: https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#componentsObject
[openapi-reference]: https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#referenceObject
[openapi-generator]: https://github.com/OpenAPITools/openapi-generator
[swagger-codegen]: https://github.com/swagger-api/swagger-codegen
[info-object]: https://swagger.io/specification/#infoObject
[drf-spectacular]: https://drf-spectacular.readthedocs.io/en/latest/readme.html