Prepare for 1.1.0 (#30)

* refactor the view inspection process to be more modular and allow recursive customization
* add operation_id argument to @swagger_auto_
* add inspections for min/max validators
* add support for URLPathVersioning and NamespaceVersioning
* integrate with djangorestframework-camel-case
* fix bugs, improve tests and documentation
This commit is contained in:
Cristi Vîjdea
2017-12-26 22:50:59 +01:00
committed by GitHub
parent 73adc49b2c
commit c89f96fcb0
52 changed files with 2167 additions and 854 deletions
+18
View File
@@ -0,0 +1,18 @@
.versionadded, .versionchanged, .deprecated {
font-family: "Roboto", Corbel, Avenir, "Lucida Grande", "Lucida Sans", sans-serif;
padding: 10px 13px;
border: 1px solid rgb(137, 191, 4);
border-radius: 4px;
margin-bottom: 10px;
}
.versionmodified {
font-weight: bold;
display: block;
}
.versionadded p, .versionchanged p, .deprecated p,
/*override fucking !important by being more specific */
.rst-content dl .versionadded p, .rst-content dl .versionchanged p {
margin: 0 !important;
}
+4
View File
@@ -0,0 +1,4 @@
{% extends "!layout.html" %}
{% block extrahead %}
<meta name="google-site-verification" content="saewLzcrUS1lAAgNVIikKWc3DUbFcE-TWtpyw3AW8CA" />
{% endblock %}
+15
View File
@@ -3,6 +3,21 @@ Changelog
#########
*********
**1.1.0**
*********
- **ADDED:** added support for APIs versioned with ``URLPathVersioning`` or ``NamespaceVersioning``
- **ADDED:** added ability to recursively customize schema generation
:ref:`using pluggable inspector classes <custom-spec-inspectors>`
- **ADDED:** added ``operation_id`` parameter to :func:`@swagger_auto_schema <.swagger_auto_schema>`
- **ADDED:** integration with `djangorestframework-camel-case
<https://github.com/vbabiy/djangorestframework-camel-case>`_ (:issue:`28`)
- **IMPROVED:** strings, arrays and integers will now have min/max validation attributes inferred from the
field-level validators
- **FIXED:** fixed a bug that caused ``title`` to never be generated for Schemas; ``title`` is now correctly
populated from the field's ``label`` property
*********
**1.0.6**
*********
+44 -3
View File
@@ -3,6 +3,7 @@
#
# drf-yasg documentation build configuration file, created by
# sphinx-quickstart on Sun Dec 10 15:20:34 2017.
import inspect
import os
import re
import sys
@@ -68,9 +69,6 @@ pygments_style = 'sphinx'
modindex_common_prefix = ['drf_yasg.']
# If true, `todo` and `todoList` produce output, else they produce nothing.
todo_include_todos = False
# -- Options for HTML output ----------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
@@ -186,18 +184,23 @@ nitpick_ignore = [
('py:obj', 'callable'),
('py:obj', 'type'),
('py:obj', 'OrderedDict'),
('py:obj', 'None'),
('py:obj', 'coreapi.Field'),
('py:obj', 'BaseFilterBackend'),
('py:obj', 'BasePagination'),
('py:obj', 'Request'),
('py:obj', 'rest_framework.request.Request'),
('py:obj', 'rest_framework.serializers.Field'),
('py:obj', 'serializers.Field'),
('py:obj', 'serializers.BaseSerializer'),
('py:obj', 'Serializer'),
('py:obj', 'BaseSerializer'),
('py:obj', 'APIView'),
]
# TODO: inheritance aliases in sphinx 1.7
# even though the package should be already installed, the sphinx build on RTD
# for some reason needs the sources dir to be in the path in order for viewcode to work
sys.path.insert(0, os.path.abspath('../src'))
@@ -215,6 +218,40 @@ import drf_yasg.views # noqa: E402
drf_yasg.views.SchemaView = drf_yasg.views.get_schema_view(None)
# monkey patch to stop sphinx from trying to find classes by their real location instead of the
# top-level __init__ alias; this allows us to document only `drf_yasg.inspectors` and avoid broken references or
# double documenting
import drf_yasg.inspectors # noqa: E402
def redirect_cls(cls):
if cls.__module__.startswith('drf_yasg.inspectors'):
return getattr(drf_yasg.inspectors, cls.__name__)
return cls
for cls_name in drf_yasg.inspectors.__all__:
# first pass - replace all classes' module with the top level module
real_cls = getattr(drf_yasg.inspectors, cls_name)
if not inspect.isclass(real_cls):
continue
patched_dict = dict(real_cls.__dict__)
patched_dict.update({'__module__': 'drf_yasg.inspectors'})
patched_cls = type(cls_name, real_cls.__bases__, patched_dict)
setattr(drf_yasg.inspectors, cls_name, patched_cls)
for cls_name in drf_yasg.inspectors.__all__:
# second pass - replace the inheritance bases for all classes to point to the new clean classes
real_cls = getattr(drf_yasg.inspectors, cls_name)
if not inspect.isclass(real_cls):
continue
patched_bases = tuple(redirect_cls(base) for base in real_cls.__bases__)
patched_cls = type(cls_name, patched_bases, dict(real_cls.__dict__))
setattr(drf_yasg.inspectors, cls_name, patched_cls)
# custom interpreted role for linking to GitHub issues and pull requests
# use as :issue:`14` or :pr:`17`
gh_issue_uri = "https://github.com/axnsan12/drf-yasg/issues/{}"
@@ -273,3 +310,7 @@ def role_github_pull_request_or_issue(name, rawtext, text, lineno, inliner, opti
roles.register_local_role('pr', role_github_pull_request_or_issue)
roles.register_local_role('issue', role_github_pull_request_or_issue)
roles.register_local_role('ghuser', role_github_user)
def setup(app):
app.add_stylesheet('css/style.css')
+136 -3
View File
@@ -249,15 +249,63 @@ Where you can use the :func:`@swagger_auto_schema <.swagger_auto_schema>` decora
However, do note that both of the methods above can lead to unexpected (and maybe surprising) results by
replacing/decorating methods on the base class itself.
********************************
Serializer ``Meta`` nested class
********************************
You can define some per-serializer options by adding a ``Meta`` class to your serializer, e.g.:
.. code:: python
class WhateverSerializer(Serializer):
...
class Meta:
... options here ...
Currently, the only option you can add here is
* ``ref_name`` - a string which will be used as the model definition name for this serializer class; setting it to
``None`` will force the serializer to be generated as an inline model everywhere it is used
*************************
Subclassing and extending
*************************
For more advanced control you can subclass :class:`.SwaggerAutoSchema` - see the documentation page for a list of
methods you can override.
---------------------
``SwaggerAutoSchema``
---------------------
For more advanced control you can subclass :class:`~.inspectors.SwaggerAutoSchema` - see the documentation page
for a list of methods you can override.
You can put your custom subclass to use by setting it on a view method using the
:func:`@swagger_auto_schema <.swagger_auto_schema>` decorator described above.
:ref:`@swagger_auto_schema <custom-spec-swagger-auto-schema>` decorator described above, by setting it as a
class-level attribute named ``swagger_schema`` on the view class, or
:ref:`globally via settings <default-class-settings>`.
For example, to generate all operation IDs as camel case, you could do:
.. code:: python
from inflection import camelize
class CamelCaseOperationIDAutoSchema(SwaggerAutoSchema):
def get_operation_id(self, operation_keys):
operation_id = super(CamelCaseOperationIDAutoSchema, self).get_operation_id(operation_keys)
return camelize(operation_id, uppercase_first_letter=False)
SWAGGER_SETTINGS = {
'DEFAULT_AUTO_SCHEMA_CLASS': 'path.to.CamelCaseOperationIDAutoSchema',
...
}
--------------------------
``OpenAPISchemaGenerator``
--------------------------
If you need to control things at a higher level than :class:`.Operation` objects (e.g. overall document structure,
vendor extensions in metadata) you can also subclass :class:`.OpenAPISchemaGenerator` - again, see the documentation
@@ -265,3 +313,88 @@ page for a list of its methods.
This custom generator can be put to use by setting it as the :attr:`.generator_class` of a :class:`.SchemaView` using
:func:`.get_schema_view`.
.. _custom-spec-inspectors:
---------------------
``Inspector`` classes
---------------------
.. versionadded:: 1.1
For customizing behavior related to specific field, serializer, filter or paginator classes you can implement the
:class:`~.inspectors.FieldInspector`, :class:`~.inspectors.SerializerInspector`, :class:`~.inspectors.FilterInspector`,
:class:`~.inspectors.PaginatorInspector` classes and use them with
:ref:`@swagger_auto_schema <custom-spec-swagger-auto-schema>` or one of the
:ref:`related settings <default-class-settings>`.
A :class:`~.inspectors.FilterInspector` that adds a description to all ``DjangoFilterBackend`` parameters could be
implemented like so:
.. code:: python
class DjangoFilterDescriptionInspector(CoreAPICompatInspector):
def get_filter_parameters(self, filter_backend):
if isinstance(filter_backend, DjangoFilterBackend):
result = super(DjangoFilterDescriptionInspector, self).get_filter_parameters(filter_backend)
for param in result:
if not param.get('description', ''):
param.description = "Filter the returned list by {field_name}".format(field_name=param.name)
return result
return NotHandled
@method_decorator(name='list', decorator=swagger_auto_schema(
filter_inspectors=[DjangoFilterDescriptionInspector]
))
class ArticleViewSet(viewsets.ModelViewSet):
filter_backends = (DjangoFilterBackend,)
filter_fields = ('title',)
...
A second example, of a :class:`~.inspectors.FieldInspector` that removes the ``title`` attribute from all generated
:class:`.Schema` objects:
.. code:: python
class NoSchemaTitleInspector(FieldInspector):
def process_result(self, result, method_name, obj, **kwargs):
# remove the `title` attribute of all Schema objects
if isinstance(result, openapi.Schema.OR_REF):
# traverse any references and alter the Schema object in place
schema = openapi.resolve_ref(result, self.components)
schema.pop('title', None)
# no ``return schema`` here, because it would mean we always generate
# an inline `object` instead of a definition reference
# return back the same object that we got - i.e. a reference if we got a reference
return result
class NoTitleAutoSchema(SwaggerAutoSchema):
field_inspectors = [NoSchemaTitleInspector] + swagger_settings.DEFAULT_FIELD_INSPECTORS
class ArticleViewSet(viewsets.ModelViewSet):
swagger_schema = NoTitleAutoSchema
...
.. Note::
A note on references - :class:`.Schema` objects are sometimes output by reference (:class:`.SchemaRef`); in fact,
that is how named models are implemented in OpenAPI:
- in the output swagger document there is a ``definitions`` section containing :class:`.Schema` objects for all
models
- every usage of a model refers to that single :class:`.Schema` object - for example, in the ArticleViewSet
above, all requests and responses containg an ``Article`` model would refer to the same schema definition by a
``'$ref': '#/definitions/Article'``
This is implemented by only generating **one** :class:`.Schema` object for every serializer **class** encountered.
This means that you should generally avoid view or method-specific ``FieldInspector``\ s if you are dealing with
references (a.k.a named models), because you can never know which view will be the first to generate the schema
for a given serializer.
+3 -9
View File
@@ -1,14 +1,6 @@
drf\_yasg package
====================
drf\_yasg\.app\_settings
----------------------------------
.. automodule:: drf_yasg.app_settings
:members:
:undoc-members:
:show-inheritance:
drf\_yasg\.codecs
---------------------------
@@ -16,7 +8,7 @@ drf\_yasg\.codecs
:members:
:undoc-members:
:show-inheritance:
:exclude-members: SaneYamlDumper
:exclude-members: SaneYamlDumper,SaneYamlLoader
drf\_yasg\.errors
---------------------------
@@ -37,6 +29,8 @@ drf\_yasg\.generators
drf\_yasg\.inspectors
-------------------------------
.. autodata:: drf_yasg.inspectors.NotHandled
.. automodule:: drf_yasg.inspectors
:members:
:undoc-members:
+54
View File
@@ -37,6 +37,60 @@ The possible settings and their default values are as follows:
``SWAGGER_SETTINGS``
********************
.. _default-class-settings:
Default classes
===============
DEFAULT_AUTO_SCHEMA_CLASS
-------------------------
:class:`~.inspectors.ViewInspector` subclass that will be used by default for generating :class:`.Operation`
objects when iterating over endpoints. Can be overriden by using the `auto_schema` argument of
:func:`@swagger_auto_schema <.swagger_auto_schema>` or by a ``swagger_schema`` attribute on the view class.
**Default**: :class:`drf_yasg.inspectors.SwaggerAutoSchema`
DEFAULT_FIELD_INSPECTORS
------------------------
List of :class:`~.inspectors.FieldInspector` subclasses that will be used by default for inspecting serializers and
serializer fields. Field inspectors given to :func:`@swagger_auto_schema <.swagger_auto_schema>` will be prepended
to this list.
**Default**: ``[`` |br| \
:class:`'drf_yasg.inspectors.CamelCaseJSONFilter' <.inspectors.CamelCaseJSONFilter>`, |br| \
:class:`'drf_yasg.inspectors.ReferencingSerializerInspector' <.inspectors.ReferencingSerializerInspector>`, |br| \
:class:`'drf_yasg.inspectors.RelatedFieldInspector' <.inspectors.RelatedFieldInspector>`, |br| \
:class:`'drf_yasg.inspectors.ChoiceFieldInspector' <.inspectors.ChoiceFieldInspector>`, |br| \
:class:`'drf_yasg.inspectors.FileFieldInspector' <.inspectors.FileFieldInspector>`, |br| \
:class:`'drf_yasg.inspectors.DictFieldInspector' <.inspectors.DictFieldInspector>`, |br| \
:class:`'drf_yasg.inspectors.SimpleFieldInspector' <.inspectors.SimpleFieldInspector>`, |br| \
:class:`'drf_yasg.inspectors.StringDefaultFieldInspector' <.inspectors.StringDefaultFieldInspector>`, |br| \
``]``
DEFAULT_FILTER_INSPECTORS
-------------------------
List of :class:`~.inspectors.FilterInspector` subclasses that will be used by default for inspecting filter backends.
Filter inspectors given to :func:`@swagger_auto_schema <.swagger_auto_schema>` will be prepended to this list.
**Default**: ``[`` |br| \
:class:`'drf_yasg.inspectors.CoreAPICompatInspector' <.inspectors.CoreAPICompatInspector>`, |br| \
``]``
DEFAULT_PAGINATOR_INSPECTORS
----------------------------
List of :class:`~.inspectors.PaginatorInspector` subclasses that will be used by default for inspecting paginators.
Paginator inspectors given to :func:`@swagger_auto_schema <.swagger_auto_schema>` will be prepended to this list.
**Default**: ``[`` |br| \
:class:`'drf_yasg.inspectors.DjangoRestResponsePagination' <.inspectors.DjangoRestResponsePagination>`, |br| \
:class:`'drf_yasg.inspectors.CoreAPICompatInspector' <.inspectors.CoreAPICompatInspector>`, |br| \
``]``
Authorization
=============