Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 10c7e22940 | |||
| 6c497b32b4 | |||
| 73c60a8fda | |||
| 97082e8898 | |||
| 743396617e | |||
| 97cd1b63d9 | |||
| 71dee6eb45 | |||
| 7fa0cc0639 | |||
| e8c688f981 | |||
| c01a6ead26 | |||
| 05901e99aa | |||
| a211184478 | |||
| a3e81ef7f6 |
+1
-1
@@ -10,7 +10,7 @@ License
|
|||||||
BSD 3-Clause License
|
BSD 3-Clause License
|
||||||
********************
|
********************
|
||||||
|
|
||||||
Copyright (c) 2017, Cristian V. <cristi@cvjd.me> |br|\ All rights reserved.
|
Copyright (c) 2018, Cristian V. <cristi@cvjd.me> |br|\ All rights reserved.
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
Redistribution and use in source and binary forms, with or without
|
||||||
modification, are permitted provided that the following conditions are met:
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|||||||
+29
-2
@@ -2,12 +2,39 @@
|
|||||||
Changelog
|
Changelog
|
||||||
#########
|
#########
|
||||||
|
|
||||||
|
*********
|
||||||
|
**1.4.1**
|
||||||
|
*********
|
||||||
|
|
||||||
|
- **FIXED:** the ``coerce_to_string`` is now respected when setting the type, default value and min/max values of
|
||||||
|
``DecimalField`` in the OpenAPI schema (:issue:`62`)
|
||||||
|
- **FIXED:** error responses from web UI views are now rendered with ``TemplateHTMLRenderer`` instead of throwing
|
||||||
|
confusing errors (:issue:`58`)
|
||||||
|
- **IMPROVED:** updated ``swagger-ui`` to version 3.10.0
|
||||||
|
- **IMPROVED:** updated ``ReDoc`` to version 1.21.0
|
||||||
|
|
||||||
|
*********
|
||||||
|
**1.4.0**
|
||||||
|
*********
|
||||||
|
|
||||||
|
- **ADDED:** added settings for OAuth2 client configuration in ``swagger-ui`` (:issue:`53`)
|
||||||
|
- **IMPROVED:** updated ``swagger-ui`` to version 3.9.3
|
||||||
|
|
||||||
|
*********
|
||||||
|
**1.3.1**
|
||||||
|
*********
|
||||||
|
|
||||||
|
- **FIXED:** fixed a bug that would sometimes cause endpoints to wrongly be output as form operations (:issue:`50`)
|
||||||
|
- **IMPROVED:** added generation of ``produces`` based on renderer classes
|
||||||
|
- **IMPROVED:** added generation of top-level ``consumes`` and ``produces`` based on
|
||||||
|
``DEFAULT_PARSER_CLASSES`` and ``DEFAULT_RENDERER_CLASSES`` (:issue:`48`)
|
||||||
|
|
||||||
*********
|
*********
|
||||||
**1.3.0**
|
**1.3.0**
|
||||||
*********
|
*********
|
||||||
|
|
||||||
- **ADDED:** security requirements are now correctly set and can be customized; this should fix
|
- **ADDED:** security requirements are now correctly set and can be customized; this should fix problems related
|
||||||
problems related to authentication in ``swagger-ui`` Try it out! (:issue:`50`, :pr:`54`)
|
to authentication in ``swagger-ui`` Try it out! (:issue:`50`, :pr:`54`)
|
||||||
- **IMPROVED:** updated ``swagger-ui`` to version 3.9.2
|
- **IMPROVED:** updated ``swagger-ui`` to version 3.9.2
|
||||||
- **IMPROVED:** updated ``ReDoc`` to version 1.20.0
|
- **IMPROVED:** updated ``ReDoc`` to version 1.20.0
|
||||||
- **FIXED:** fixed an exception caused by a warning in get_path_from_regex (:pr:`49`, thanks to :ghuser:`blueyed`)
|
- **FIXED:** fixed an exception caused by a warning in get_path_from_regex (:pr:`49`, thanks to :ghuser:`blueyed`)
|
||||||
|
|||||||
+24
-24
@@ -39,7 +39,7 @@ master_doc = 'index'
|
|||||||
|
|
||||||
# General information about the project.
|
# General information about the project.
|
||||||
project = 'drf-yasg'
|
project = 'drf-yasg'
|
||||||
copyright = '2017, Cristi V.'
|
copyright = '2018, Cristi V.'
|
||||||
author = 'Cristi V.'
|
author = 'Cristi V.'
|
||||||
|
|
||||||
# The version info for the project you're documenting, acts as replacement for
|
# The version info for the project you're documenting, acts as replacement for
|
||||||
@@ -162,6 +162,18 @@ autodoc_mock_imports = []
|
|||||||
|
|
||||||
nitpick_ignore = [
|
nitpick_ignore = [
|
||||||
('py:class', 'object'),
|
('py:class', 'object'),
|
||||||
|
('py:class', 'bool'),
|
||||||
|
('py:class', 'dict'),
|
||||||
|
('py:class', 'list'),
|
||||||
|
('py:class', 'str'),
|
||||||
|
('py:class', 'int'),
|
||||||
|
('py:class', 'bytes'),
|
||||||
|
('py:class', 'tuple'),
|
||||||
|
('py:class', 'callable'),
|
||||||
|
('py:class', 'type'),
|
||||||
|
('py:class', 'OrderedDict'),
|
||||||
|
('py:class', 'None'),
|
||||||
|
|
||||||
('py:class', 'Exception'),
|
('py:class', 'Exception'),
|
||||||
('py:class', 'collections.OrderedDict'),
|
('py:class', 'collections.OrderedDict'),
|
||||||
|
|
||||||
@@ -174,29 +186,17 @@ nitpick_ignore = [
|
|||||||
('py:class', 'OpenAPICodecJson'),
|
('py:class', 'OpenAPICodecJson'),
|
||||||
('py:class', 'OpenAPISchemaGenerator'),
|
('py:class', 'OpenAPISchemaGenerator'),
|
||||||
|
|
||||||
('py:obj', 'bool'),
|
('py:class', 'coreapi.Field'),
|
||||||
('py:obj', 'dict'),
|
('py:class', 'BaseFilterBackend'),
|
||||||
('py:obj', 'list'),
|
('py:class', 'BasePagination'),
|
||||||
('py:obj', 'str'),
|
('py:class', 'Request'),
|
||||||
('py:obj', 'int'),
|
('py:class', 'rest_framework.request.Request'),
|
||||||
('py:obj', 'bytes'),
|
('py:class', 'rest_framework.serializers.Field'),
|
||||||
('py:obj', 'tuple'),
|
('py:class', 'serializers.Field'),
|
||||||
('py:obj', 'callable'),
|
('py:class', 'serializers.BaseSerializer'),
|
||||||
('py:obj', 'type'),
|
('py:class', 'Serializer'),
|
||||||
('py:obj', 'OrderedDict'),
|
('py:class', 'BaseSerializer'),
|
||||||
('py:obj', 'None'),
|
('py:class', 'APIView'),
|
||||||
|
|
||||||
('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'),
|
|
||||||
]
|
]
|
||||||
|
|
||||||
# even though the package should be already installed, the sphinx build on RTD
|
# even though the package should be already installed, the sphinx build on RTD
|
||||||
|
|||||||
+25
-2
@@ -9,6 +9,31 @@ Custom schema generation
|
|||||||
If the default spec generation does not quite match what you were hoping to achieve, ``drf-yasg`` provides some
|
If the default spec generation does not quite match what you were hoping to achieve, ``drf-yasg`` provides some
|
||||||
custom behavior hooks by default.
|
custom behavior hooks by default.
|
||||||
|
|
||||||
|
.. _custom-spec-excluding-endpoints:
|
||||||
|
|
||||||
|
*******************
|
||||||
|
Excluding endpoints
|
||||||
|
*******************
|
||||||
|
|
||||||
|
You can prevent a view from being included in the Swagger view by setting its class-level ``swagger_schema``
|
||||||
|
attribute to ``None``, or you can prevent an operation from being included by setting its ``auto_schema`` override
|
||||||
|
to none in :ref:`@swagger_auto_schema <custom-spec-swagger-auto-schema>`:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
class UserList(APIView):
|
||||||
|
swagger_schema = None
|
||||||
|
|
||||||
|
# all methods of the UserList class will be excluded
|
||||||
|
...
|
||||||
|
|
||||||
|
# only the GET method will be shown in Swagger
|
||||||
|
@swagger_auto_schema(method='put', auto_schema=None)
|
||||||
|
@swagger_auto_schema(methods=['get'], ...)
|
||||||
|
@api_view(['GET', 'PUT'])
|
||||||
|
def user_detail(request, pk):
|
||||||
|
pass
|
||||||
|
|
||||||
.. _custom-spec-swagger-auto-schema:
|
.. _custom-spec-swagger-auto-schema:
|
||||||
|
|
||||||
**************************************
|
**************************************
|
||||||
@@ -200,8 +225,6 @@ This custom generator can be put to use by setting it as the :attr:`.generator_c
|
|||||||
``Inspector`` classes
|
``Inspector`` classes
|
||||||
---------------------
|
---------------------
|
||||||
|
|
||||||
.. versionadded:: 1.1
|
|
||||||
|
|
||||||
For customizing behavior related to specific field, serializer, filter or paginator classes you can implement the
|
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.FieldInspector`, :class:`~.inspectors.SerializerInspector`, :class:`~.inspectors.FilterInspector`,
|
||||||
:class:`~.inspectors.PaginatorInspector` classes and use them with
|
:class:`~.inspectors.PaginatorInspector` classes and use them with
|
||||||
|
|||||||
+19
-3
@@ -6,7 +6,6 @@
|
|||||||
Functional overview
|
Functional overview
|
||||||
**********************
|
**********************
|
||||||
|
|
||||||
|
|
||||||
------------------------------
|
------------------------------
|
||||||
OpenAPI specification overview
|
OpenAPI specification overview
|
||||||
------------------------------
|
------------------------------
|
||||||
@@ -155,9 +154,26 @@ This section describes where information is sourced from when using the default
|
|||||||
|
|
||||||
Other versioning schemes are not presently supported.
|
Other versioning schemes are not presently supported.
|
||||||
|
|
||||||
|
---------------------
|
||||||
|
A note on limitations
|
||||||
|
---------------------
|
||||||
|
|
||||||
.. versionadded:: 1.2
|
When schema generation is requested, available endpoints are inspected by enumeration all the routes registered in
|
||||||
Base path and versioning support.
|
Django's urlconf. Each registered view is then artificially instantiated for introspection, and it is this step that
|
||||||
|
brings some limitations to what can be done:
|
||||||
|
|
||||||
|
* the ``request`` the view sees will always be the request made against the schema view endpoint
|
||||||
|
- e.g. ``GET /swagger.yaml``
|
||||||
|
* path parameters will not be filled
|
||||||
|
|
||||||
|
This means that you could get surprizing results if your ``get_serializer`` or ``get_serializer_class`` methods
|
||||||
|
depend on the incoming request, call ``get_object`` or in general depend on any stateful logic. You can prevent this
|
||||||
|
in a few ways:
|
||||||
|
|
||||||
|
* provide a fixed serializer for request and response body introspection using
|
||||||
|
:ref:`@swagger_auto_schema <custom-spec-swagger-auto-schema>`, to prevent ``get_serializer`` from being called on
|
||||||
|
the view
|
||||||
|
* :ref:`exclude your endpoint from introspection <custom-spec-excluding-endpoints>`
|
||||||
|
|
||||||
.. _SCRIPT_NAME: https://www.python.org/dev/peps/pep-0333/#environ-variables
|
.. _SCRIPT_NAME: https://www.python.org/dev/peps/pep-0333/#environ-variables
|
||||||
.. _FORCE_SCRIPT_NAME: https://docs.djangoproject.com/en/2.0/ref/settings/#force-script-name
|
.. _FORCE_SCRIPT_NAME: https://docs.djangoproject.com/en/2.0/ref/settings/#force-script-name
|
||||||
|
|||||||
@@ -41,8 +41,6 @@ You can use your custom renderer classes as kwargs to :meth:`.SchemaView.as_cach
|
|||||||
Management command
|
Management command
|
||||||
******************
|
******************
|
||||||
|
|
||||||
.. versionadded:: 1.1.1
|
|
||||||
|
|
||||||
If you only need a swagger spec file in YAML or JSON format, you can use the ``generate_swagger`` management command
|
If you only need a swagger spec file in YAML or JSON format, you can use the ``generate_swagger`` management command
|
||||||
to get it without having to start the web server:
|
to get it without having to start the web server:
|
||||||
|
|
||||||
|
|||||||
@@ -47,3 +47,19 @@ Operation-level overrides can be added using the ``security`` parameter of
|
|||||||
:ref:`@swagger_auto_schema <custom-spec-swagger-auto-schema>`.
|
:ref:`@swagger_auto_schema <custom-spec-swagger-auto-schema>`.
|
||||||
|
|
||||||
|
|
||||||
|
-------------------------------
|
||||||
|
``swagger-ui`` as OAuth2 client
|
||||||
|
-------------------------------
|
||||||
|
|
||||||
|
It is possible to configure ``swagger-ui`` to authenticate against your (or a third party) OAuth2 service when sending
|
||||||
|
"Try it out" requests. This client-side configuration does not remove the requirement of a spec-side
|
||||||
|
:ref:`security definiiton <security-definitions-settings>`, but merely allows you to test OAuth2 APIs using
|
||||||
|
``swagger-ui`` as a client.
|
||||||
|
|
||||||
|
**DISCLAIMER**: this setup is very poorly tested as I do not currently implement OAuth in any of my projects. All
|
||||||
|
contributions relating to documentation, bugs, mistakes or anything else are welcome as an issue or pull request. The
|
||||||
|
settings described below were added as a result of discussion in issue :issue:`53`.
|
||||||
|
|
||||||
|
The settings of interest can be found on the :ref:`settings page <oauth2-settings>`. Configuration options are similar
|
||||||
|
to most OAuth client setups like web or mobile applications. Reading the relevant ``swagger-ui`` docmentation linked
|
||||||
|
will also probably help.
|
||||||
|
|||||||
@@ -253,6 +253,25 @@ Controls how many levels are expaned by default when showing nested models.
|
|||||||
**Default**: :python:`3` |br|
|
**Default**: :python:`3` |br|
|
||||||
*Maps to parameter*: ``defaultModelExpandDepth``
|
*Maps to parameter*: ``defaultModelExpandDepth``
|
||||||
|
|
||||||
|
.. _oauth2-settings:
|
||||||
|
|
||||||
|
OAUTH2_REDIRECT_URL
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
Used when OAuth2 authenitcation of API requests via swagger-ui is desired.
|
||||||
|
|
||||||
|
**Default**: :python:`None` |br|
|
||||||
|
*Maps to parameter*: ``oauth2RedirectUrl``
|
||||||
|
|
||||||
|
OAUTH2_CONFIG
|
||||||
|
-------------
|
||||||
|
|
||||||
|
Used when OAuth2 authenitcation of API requests via swagger-ui is desired. Provides OAuth2 configuration parameters
|
||||||
|
to the ``SwaggerUIBundle#initOAuth`` method, and must be a dictionary. See
|
||||||
|
`OAuth2 configuration <https://github.com/swagger-api/swagger-ui/blob/master/docs/usage/oauth2.md>`_.
|
||||||
|
|
||||||
|
**Default**: :python:`{}`
|
||||||
|
|
||||||
******************
|
******************
|
||||||
``REDOC_SETTINGS``
|
``REDOC_SETTINGS``
|
||||||
******************
|
******************
|
||||||
|
|||||||
Generated
+45
-42
@@ -4,9 +4,9 @@
|
|||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"argparse": {
|
"argparse": {
|
||||||
"version": "1.0.9",
|
"version": "1.0.10",
|
||||||
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.9.tgz",
|
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
|
||||||
"integrity": "sha1-c9g7wmP4bpf4zE9rrhsOkKfSLIY=",
|
"integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"sprintf-js": "1.0.3"
|
"sprintf-js": "1.0.3"
|
||||||
}
|
}
|
||||||
@@ -38,9 +38,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"commander": {
|
"commander": {
|
||||||
"version": "2.13.0",
|
"version": "2.14.1",
|
||||||
"resolved": "https://registry.npmjs.org/commander/-/commander-2.13.0.tgz",
|
"resolved": "https://registry.npmjs.org/commander/-/commander-2.14.1.tgz",
|
||||||
"integrity": "sha512-MVuS359B+YzaWqjCL/c+22gfryv+mCBPHAv3zyVI2GN8EY6IRP8VwtasXn8jyyhvvq84R4ImN1OKRtcbIasjYA==",
|
"integrity": "sha512-+YR16o3rK53SmWHU3rEM3tPAh2rwb1yPcQX5irVn7mb0gXbwuCCrnkbV5+PBfETdfg1vui07nM6PCG1zndcjQw==",
|
||||||
"optional": true
|
"optional": true
|
||||||
},
|
},
|
||||||
"core-js": {
|
"core-js": {
|
||||||
@@ -73,9 +73,9 @@
|
|||||||
"integrity": "sha1-8TyUAhQdoJ50rfTmN5jXkiBEOPI="
|
"integrity": "sha1-8TyUAhQdoJ50rfTmN5jXkiBEOPI="
|
||||||
},
|
},
|
||||||
"es6-promise": {
|
"es6-promise": {
|
||||||
"version": "4.2.2",
|
"version": "4.2.4",
|
||||||
"resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.4.tgz",
|
||||||
"integrity": "sha512-LSas5vsuA6Q4nEdf9wokY5/AJYXry98i0IzXsv49rYsgDGDNDPbqAYR1Pe23iFxygfbGZNR/5VrHXBCh2BhvUQ=="
|
"integrity": "sha512-/NdNZVJg+uZgtm9eS3O6lrOLYmQag2DjdEXuPaHlZ6RuVqgqaVZfgYCepEIKsLqwdQArOPtC3XzRLqGGfT8KQQ=="
|
||||||
},
|
},
|
||||||
"esprima": {
|
"esprima": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
@@ -126,7 +126,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.10.0.tgz",
|
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.10.0.tgz",
|
||||||
"integrity": "sha512-O2v52ffjLa9VeM43J4XocZE//WT9N0IiwDa3KSHH7Tu8CtH+1qM8SIZvnsTh6v+4yFy5KUY3BHUVwjpfAWsjIA==",
|
"integrity": "sha512-O2v52ffjLa9VeM43J4XocZE//WT9N0IiwDa3KSHH7Tu8CtH+1qM8SIZvnsTh6v+4yFy5KUY3BHUVwjpfAWsjIA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"argparse": "1.0.9",
|
"argparse": "1.0.10",
|
||||||
"esprima": "4.0.0"
|
"esprima": "4.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -145,10 +145,10 @@
|
|||||||
"requires": {
|
"requires": {
|
||||||
"call-me-maybe": "1.0.1",
|
"call-me-maybe": "1.0.1",
|
||||||
"debug": "3.1.0",
|
"debug": "3.1.0",
|
||||||
"es6-promise": "4.2.2",
|
"es6-promise": "4.2.4",
|
||||||
"js-yaml": "3.10.0",
|
"js-yaml": "3.10.0",
|
||||||
"ono": "4.0.3",
|
"ono": "4.0.3",
|
||||||
"z-schema": "3.19.0"
|
"z-schema": "3.19.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"lodash.get": {
|
"lodash.get": {
|
||||||
@@ -185,9 +185,12 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"openapi-sampler": {
|
"openapi-sampler": {
|
||||||
"version": "0.4.3",
|
"version": "1.0.0-beta.8",
|
||||||
"resolved": "https://registry.npmjs.org/openapi-sampler/-/openapi-sampler-0.4.3.tgz",
|
"resolved": "https://registry.npmjs.org/openapi-sampler/-/openapi-sampler-1.0.0-beta.8.tgz",
|
||||||
"integrity": "sha512-Ml6o1gt++ZQ4JKL344YRo/fX05yuM6C+l/mGVX2yjhu1BRKyrRK4Z46uBTKSVaag1xINBFwYG7dZdz/10AmPzA=="
|
"integrity": "sha1-v0P/R3N/xOH5iNDiCC1JeI9B3q0=",
|
||||||
|
"requires": {
|
||||||
|
"json-pointer": "0.6.0"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"perfect-scrollbar": {
|
"perfect-scrollbar": {
|
||||||
"version": "0.8.1",
|
"version": "0.8.1",
|
||||||
@@ -195,36 +198,36 @@
|
|||||||
"integrity": "sha512-RNC5tX/JMRYR+qVdJTEAWnRxw0Yf9lvbO8lTuAOvgDODkiA8lveTSkvrNMhmaGKEyimJpJl+myb/syVS9YyPuw=="
|
"integrity": "sha512-RNC5tX/JMRYR+qVdJTEAWnRxw0Yf9lvbO8lTuAOvgDODkiA8lveTSkvrNMhmaGKEyimJpJl+myb/syVS9YyPuw=="
|
||||||
},
|
},
|
||||||
"prismjs": {
|
"prismjs": {
|
||||||
"version": "1.10.0",
|
"version": "1.11.0",
|
||||||
"resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.10.0.tgz",
|
"resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.11.0.tgz",
|
||||||
"integrity": "sha1-d+UYfCrmsyU/zDEwKc8l/lN3hyE=",
|
"integrity": "sha1-KXrvM+t5Qhv9sZJzpQkspRWXDSk=",
|
||||||
"requires": {
|
"requires": {
|
||||||
"clipboard": "1.7.1"
|
"clipboard": "1.7.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"process-nextick-args": {
|
"process-nextick-args": {
|
||||||
"version": "1.0.7",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz",
|
||||||
"integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M="
|
"integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw=="
|
||||||
},
|
},
|
||||||
"readable-stream": {
|
"readable-stream": {
|
||||||
"version": "2.3.3",
|
"version": "2.3.4",
|
||||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.4.tgz",
|
||||||
"integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==",
|
"integrity": "sha512-vuYxeWYM+fde14+rajzqgeohAI7YoJcHE7kXDAc4Nk0EbuKnJfqtY9YtRkLo/tqkuF7MsBQRhPnPeyjYITp3ZQ==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"core-util-is": "1.0.2",
|
"core-util-is": "1.0.2",
|
||||||
"inherits": "2.0.3",
|
"inherits": "2.0.3",
|
||||||
"isarray": "1.0.0",
|
"isarray": "1.0.0",
|
||||||
"process-nextick-args": "1.0.7",
|
"process-nextick-args": "2.0.0",
|
||||||
"safe-buffer": "5.1.1",
|
"safe-buffer": "5.1.1",
|
||||||
"string_decoder": "1.0.3",
|
"string_decoder": "1.0.3",
|
||||||
"util-deprecate": "1.0.2"
|
"util-deprecate": "1.0.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"redoc": {
|
"redoc": {
|
||||||
"version": "1.20.0",
|
"version": "1.21.0",
|
||||||
"resolved": "https://registry.npmjs.org/redoc/-/redoc-1.20.0.tgz",
|
"resolved": "https://registry.npmjs.org/redoc/-/redoc-1.21.0.tgz",
|
||||||
"integrity": "sha1-1c16xoQKJ8/7RzvSiYAFUq+CHq8=",
|
"integrity": "sha1-RY8E7b7MqyVbQORhZA0eRG949N0=",
|
||||||
"requires": {
|
"requires": {
|
||||||
"core-js": "2.5.3",
|
"core-js": "2.5.3",
|
||||||
"dropkickjs": "2.1.10",
|
"dropkickjs": "2.1.10",
|
||||||
@@ -234,9 +237,9 @@
|
|||||||
"json-schema-ref-parser": "3.3.1",
|
"json-schema-ref-parser": "3.3.1",
|
||||||
"lunr": "1.0.0",
|
"lunr": "1.0.0",
|
||||||
"mark.js": "8.11.1",
|
"mark.js": "8.11.1",
|
||||||
"openapi-sampler": "0.4.3",
|
"openapi-sampler": "1.0.0-beta.8",
|
||||||
"perfect-scrollbar": "0.8.1",
|
"perfect-scrollbar": "0.8.1",
|
||||||
"prismjs": "1.10.0",
|
"prismjs": "1.11.0",
|
||||||
"remarkable": "1.7.1",
|
"remarkable": "1.7.1",
|
||||||
"scrollparent": "2.0.1",
|
"scrollparent": "2.0.1",
|
||||||
"slugify": "1.2.9",
|
"slugify": "1.2.9",
|
||||||
@@ -298,7 +301,7 @@
|
|||||||
"requires": {
|
"requires": {
|
||||||
"builtin-status-codes": "3.0.0",
|
"builtin-status-codes": "3.0.0",
|
||||||
"inherits": "2.0.3",
|
"inherits": "2.0.3",
|
||||||
"readable-stream": "2.3.3",
|
"readable-stream": "2.3.4",
|
||||||
"to-arraybuffer": "1.0.1",
|
"to-arraybuffer": "1.0.1",
|
||||||
"xtend": "4.0.1"
|
"xtend": "4.0.1"
|
||||||
}
|
}
|
||||||
@@ -312,9 +315,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"swagger-ui-dist": {
|
"swagger-ui-dist": {
|
||||||
"version": "3.9.2",
|
"version": "3.10.0",
|
||||||
"resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-3.9.2.tgz",
|
"resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-3.10.0.tgz",
|
||||||
"integrity": "sha1-XuK7G2IFPC3ydGnlVZDGX8AOidQ="
|
"integrity": "sha1-ilrzP/ImPHFaFD9z8qjUfVOZQ+8="
|
||||||
},
|
},
|
||||||
"tiny-emitter": {
|
"tiny-emitter": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
@@ -348,9 +351,9 @@
|
|||||||
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
|
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
|
||||||
},
|
},
|
||||||
"validator": {
|
"validator": {
|
||||||
"version": "9.2.0",
|
"version": "9.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/validator/-/validator-9.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/validator/-/validator-9.4.1.tgz",
|
||||||
"integrity": "sha512-6Ij4Eo0KM4LkR0d0IegOwluG5453uqT5QyF5SV5Ezvm8/zmkKI/L4eoraafZGlZPC9guLkwKzgypcw8VGWWnGA=="
|
"integrity": "sha512-YV5KjzvRmSyJ1ee/Dm5UED0G+1L4GZnLN3w6/T+zZm8scVua4sOhYKWTUrKa0H/tMiJyO9QLHMPN+9mB/aMunA=="
|
||||||
},
|
},
|
||||||
"xtend": {
|
"xtend": {
|
||||||
"version": "4.0.1",
|
"version": "4.0.1",
|
||||||
@@ -358,14 +361,14 @@
|
|||||||
"integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68="
|
"integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68="
|
||||||
},
|
},
|
||||||
"z-schema": {
|
"z-schema": {
|
||||||
"version": "3.19.0",
|
"version": "3.19.1",
|
||||||
"resolved": "https://registry.npmjs.org/z-schema/-/z-schema-3.19.0.tgz",
|
"resolved": "https://registry.npmjs.org/z-schema/-/z-schema-3.19.1.tgz",
|
||||||
"integrity": "sha512-V94f3ODuluBS4kQLLjNhwoMek0dyIXCsvNu/A17dAyJ6sMhT5KkJQwSn07R0naByLIXJWMDk+ruMfI/3G3hS4Q==",
|
"integrity": "sha512-jPNzqmOu3+AGbb4krDODqo4QBzwUGDVzyfGyy1HtWaUnafltQotatSpxxWd6Mp0iSZOUwHU5sqKYi+U8HsHMkg==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"commander": "2.13.0",
|
"commander": "2.14.1",
|
||||||
"lodash.get": "4.4.2",
|
"lodash.get": "4.4.2",
|
||||||
"lodash.isequal": "4.5.0",
|
"lodash.isequal": "4.5.0",
|
||||||
"validator": "9.2.0"
|
"validator": "9.4.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"zone.js": {
|
"zone.js": {
|
||||||
|
|||||||
+8
-3
@@ -1,7 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "drf-yasg",
|
"name": "drf-yasg",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"redoc": "^1.20.0",
|
"redoc": "^1.21.0",
|
||||||
"swagger-ui-dist": "^3.9.2"
|
"swagger-ui-dist": "^3.10.0"
|
||||||
}
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/axnsan12/drf-yasg.git"
|
||||||
|
},
|
||||||
|
"private": true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# used by the 'docs' tox env for building the documentation
|
# used by the 'docs' tox env for building the documentation
|
||||||
Sphinx>=1.6.5
|
Sphinx>=1.7.0
|
||||||
sphinx_rtd_theme>=0.2.4
|
sphinx_rtd_theme>=0.2.4
|
||||||
Pillow>=4.3.0
|
Pillow>=4.3.0
|
||||||
readme_renderer>=17.2
|
readme_renderer>=17.2
|
||||||
|
|||||||
@@ -43,6 +43,8 @@ SWAGGER_DEFAULTS = {
|
|||||||
'SHOW_EXTENSIONS': True,
|
'SHOW_EXTENSIONS': True,
|
||||||
'DEFAULT_MODEL_RENDERING': 'model',
|
'DEFAULT_MODEL_RENDERING': 'model',
|
||||||
'DEFAULT_MODEL_DEPTH': 3,
|
'DEFAULT_MODEL_DEPTH': 3,
|
||||||
|
'OAUTH2_REDIRECT_URL': None,
|
||||||
|
'OAUTH2_CONFIG': {},
|
||||||
}
|
}
|
||||||
|
|
||||||
REDOC_DEFAULTS = {
|
REDOC_DEFAULTS = {
|
||||||
|
|||||||
+28
-12
@@ -10,13 +10,14 @@ from rest_framework.compat import URLPattern, URLResolver, get_original_route
|
|||||||
from rest_framework.schemas.generators import EndpointEnumerator as _EndpointEnumerator
|
from rest_framework.schemas.generators import EndpointEnumerator as _EndpointEnumerator
|
||||||
from rest_framework.schemas.generators import SchemaGenerator, endpoint_ordering
|
from rest_framework.schemas.generators import SchemaGenerator, endpoint_ordering
|
||||||
from rest_framework.schemas.inspectors import get_pk_description
|
from rest_framework.schemas.inspectors import get_pk_description
|
||||||
|
from rest_framework.settings import api_settings as rest_framework_settings
|
||||||
from drf_yasg.errors import SwaggerGenerationError
|
|
||||||
|
|
||||||
from . import openapi
|
from . import openapi
|
||||||
from .app_settings import swagger_settings
|
from .app_settings import swagger_settings
|
||||||
|
from .errors import SwaggerGenerationError
|
||||||
from .inspectors.field import get_basic_type_info, get_queryset_field
|
from .inspectors.field import get_basic_type_info, get_queryset_field
|
||||||
from .openapi import ReferenceResolver
|
from .openapi import ReferenceResolver
|
||||||
|
from .utils import get_consumes, get_produces
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -44,6 +45,9 @@ class EndpointEnumerator(_EndpointEnumerator):
|
|||||||
if version and version not in namespace.split(':'):
|
if version and version not in namespace.split(':'):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
if getattr(callback.cls, 'swagger_schema', object()) is None:
|
||||||
|
return False
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def replace_version(self, path, callback):
|
def replace_version(self, path, callback):
|
||||||
@@ -165,6 +169,9 @@ class OpenAPISchemaGenerator(object):
|
|||||||
self._gen = SchemaGenerator(info.title, url, info.get('description', ''), patterns, urlconf)
|
self._gen = SchemaGenerator(info.title, url, info.get('description', ''), patterns, urlconf)
|
||||||
self.info = info
|
self.info = info
|
||||||
self.version = version
|
self.version = version
|
||||||
|
self.consumes = []
|
||||||
|
self.produces = []
|
||||||
|
|
||||||
if url is None and swagger_settings.DEFAULT_API_URL is not None:
|
if url is None and swagger_settings.DEFAULT_API_URL is not None:
|
||||||
url = swagger_settings.DEFAULT_API_URL
|
url = swagger_settings.DEFAULT_API_URL
|
||||||
|
|
||||||
@@ -191,22 +198,24 @@ class OpenAPISchemaGenerator(object):
|
|||||||
"""
|
"""
|
||||||
endpoints = self.get_endpoints(request)
|
endpoints = self.get_endpoints(request)
|
||||||
components = ReferenceResolver(openapi.SCHEMA_DEFINITIONS)
|
components = ReferenceResolver(openapi.SCHEMA_DEFINITIONS)
|
||||||
|
self.consumes = get_consumes(rest_framework_settings.DEFAULT_PARSER_CLASSES)
|
||||||
|
self.produces = get_produces(rest_framework_settings.DEFAULT_RENDERER_CLASSES)
|
||||||
paths, prefix = self.get_paths(endpoints, components, request, public)
|
paths, prefix = self.get_paths(endpoints, components, request, public)
|
||||||
|
|
||||||
|
security_definitions = swagger_settings.SECURITY_DEFINITIONS
|
||||||
|
security_requirements = swagger_settings.SECURITY_REQUIREMENTS
|
||||||
|
if security_requirements is None:
|
||||||
|
security_requirements = [{security_scheme: [] for security_scheme in swagger_settings.SECURITY_DEFINITIONS}]
|
||||||
|
|
||||||
url = self.url
|
url = self.url
|
||||||
if url is None and request is not None:
|
if url is None and request is not None:
|
||||||
url = request.build_absolute_uri()
|
url = request.build_absolute_uri()
|
||||||
|
|
||||||
swagger = openapi.Swagger(
|
return openapi.Swagger(
|
||||||
info=self.info, paths=paths,
|
info=self.info, paths=paths, consumes=self.consumes or None, produces=self.produces or None,
|
||||||
|
security_definitions=security_definitions, security=security_requirements,
|
||||||
_url=url, _prefix=prefix, _version=self.version, **dict(components)
|
_url=url, _prefix=prefix, _version=self.version, **dict(components)
|
||||||
)
|
)
|
||||||
swagger.security_definitions = swagger_settings.SECURITY_DEFINITIONS
|
|
||||||
security_requirements = swagger_settings.SECURITY_REQUIREMENTS
|
|
||||||
if security_requirements is None:
|
|
||||||
security_requirements = [{security_scheme: [] for security_scheme in swagger_settings.SECURITY_DEFINITIONS}]
|
|
||||||
swagger.security = security_requirements
|
|
||||||
return swagger
|
|
||||||
|
|
||||||
def create_view(self, callback, method, request=None):
|
def create_view(self, callback, method, request=None):
|
||||||
"""Create a view instance from a view callback as registered in urlpatterns.
|
"""Create a view instance from a view callback as registered in urlpatterns.
|
||||||
@@ -330,7 +339,6 @@ class OpenAPISchemaGenerator(object):
|
|||||||
:param Request request: the request made against the schema view; can be None
|
:param Request request: the request made against the schema view; can be None
|
||||||
:rtype: openapi.Operation
|
:rtype: openapi.Operation
|
||||||
"""
|
"""
|
||||||
|
|
||||||
operation_keys = self.get_operation_keys(path[len(prefix):], method, view)
|
operation_keys = self.get_operation_keys(path[len(prefix):], method, view)
|
||||||
overrides = self.get_overrides(view, method)
|
overrides = self.get_overrides(view, method)
|
||||||
|
|
||||||
@@ -342,8 +350,16 @@ class OpenAPISchemaGenerator(object):
|
|||||||
# 3. on the swagger_auto_schema decorator
|
# 3. on the swagger_auto_schema decorator
|
||||||
view_inspector_cls = overrides.get('auto_schema', view_inspector_cls)
|
view_inspector_cls = overrides.get('auto_schema', view_inspector_cls)
|
||||||
|
|
||||||
|
if view_inspector_cls is None:
|
||||||
|
return None
|
||||||
|
|
||||||
view_inspector = view_inspector_cls(view, path, method, components, request, overrides)
|
view_inspector = view_inspector_cls(view, path, method, components, request, overrides)
|
||||||
return view_inspector.get_operation(operation_keys)
|
operation = view_inspector.get_operation(operation_keys)
|
||||||
|
if set(operation.consumes) == set(self.consumes):
|
||||||
|
del operation.consumes
|
||||||
|
if set(operation.produces) == set(self.produces):
|
||||||
|
del operation.produces
|
||||||
|
return operation
|
||||||
|
|
||||||
def get_path_item(self, path, view_cls, operations):
|
def get_path_item(self, path, view_cls, operations):
|
||||||
"""Get a :class:`.PathItem` object that describes the parameters and operations related to a single path in the
|
"""Get a :class:`.PathItem` object that describes the parameters and operations related to a single path in the
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ from rest_framework import serializers
|
|||||||
from rest_framework.utils import encoders, json
|
from rest_framework.utils import encoders, json
|
||||||
|
|
||||||
from .. import openapi
|
from .. import openapi
|
||||||
from ..utils import is_list_view
|
from ..utils import decimal_as_float, is_list_view
|
||||||
|
|
||||||
#: Sentinel value that inspectors must return to signal that they do not know how to handle an object
|
#: Sentinel value that inspectors must return to signal that they do not know how to handle an object
|
||||||
NotHandled = object()
|
NotHandled = object()
|
||||||
@@ -224,6 +224,8 @@ class FieldInspector(BaseInspector):
|
|||||||
# JSON roundtrip ensures that the value is valid JSON;
|
# JSON roundtrip ensures that the value is valid JSON;
|
||||||
# for example, sets and tuples get transformed into lists
|
# for example, sets and tuples get transformed into lists
|
||||||
default = json.loads(json.dumps(default, cls=encoders.JSONEncoder))
|
default = json.loads(json.dumps(default, cls=encoders.JSONEncoder))
|
||||||
|
if decimal_as_float(field):
|
||||||
|
default = float(default)
|
||||||
except Exception: # pragma: no cover
|
except Exception: # pragma: no cover
|
||||||
logger.warning("'default' on schema for %s will not be set because "
|
logger.warning("'default' on schema for %s will not be set because "
|
||||||
"to_representation raised an exception", field, exc_info=True)
|
"to_representation raised an exception", field, exc_info=True)
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import operator
|
import operator
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
|
from decimal import Decimal
|
||||||
|
|
||||||
from django.core import validators
|
from django.core import validators
|
||||||
from django.db import models
|
from django.db import models
|
||||||
@@ -8,7 +9,7 @@ from rest_framework.settings import api_settings as rest_framework_settings
|
|||||||
|
|
||||||
from .. import openapi
|
from .. import openapi
|
||||||
from ..errors import SwaggerGenerationError
|
from ..errors import SwaggerGenerationError
|
||||||
from ..utils import filter_none
|
from ..utils import decimal_as_float, filter_none
|
||||||
from .base import FieldInspector, NotHandled, SerializerInspector
|
from .base import FieldInspector, NotHandled, SerializerInspector
|
||||||
|
|
||||||
|
|
||||||
@@ -258,18 +259,29 @@ def find_limits(field):
|
|||||||
if isinstance(field, field_class)
|
if isinstance(field, field_class)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
if isinstance(field, serializers.DecimalField) and not decimal_as_float(field):
|
||||||
|
return limits
|
||||||
|
|
||||||
for validator in field.validators:
|
for validator in field.validators:
|
||||||
if not hasattr(validator, 'limit_value'):
|
if not hasattr(validator, 'limit_value'):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
limit_value = validator.limit_value
|
||||||
|
if isinstance(limit_value, Decimal) and decimal_as_float(field):
|
||||||
|
limit_value = float(limit_value)
|
||||||
|
|
||||||
for validator_class, attr, improves in applicable_limits:
|
for validator_class, attr, improves in applicable_limits:
|
||||||
if isinstance(validator, validator_class):
|
if isinstance(validator, validator_class):
|
||||||
if attr not in limits or improves(validator.limit_value, limits[attr]):
|
if attr not in limits or improves(limit_value, limits[attr]):
|
||||||
limits[attr] = validator.limit_value
|
limits[attr] = limit_value
|
||||||
|
|
||||||
return OrderedDict(sorted(limits.items()))
|
return OrderedDict(sorted(limits.items()))
|
||||||
|
|
||||||
|
|
||||||
|
def decimal_field_type(field):
|
||||||
|
return openapi.TYPE_NUMBER if decimal_as_float(field) else openapi.TYPE_STRING
|
||||||
|
|
||||||
|
|
||||||
model_field_to_basic_type = [
|
model_field_to_basic_type = [
|
||||||
(models.AutoField, (openapi.TYPE_INTEGER, None)),
|
(models.AutoField, (openapi.TYPE_INTEGER, None)),
|
||||||
(models.BinaryField, (openapi.TYPE_STRING, openapi.FORMAT_BINARY)),
|
(models.BinaryField, (openapi.TYPE_STRING, openapi.FORMAT_BINARY)),
|
||||||
@@ -277,7 +289,7 @@ model_field_to_basic_type = [
|
|||||||
(models.NullBooleanField, (openapi.TYPE_BOOLEAN, None)),
|
(models.NullBooleanField, (openapi.TYPE_BOOLEAN, None)),
|
||||||
(models.DateTimeField, (openapi.TYPE_STRING, openapi.FORMAT_DATETIME)),
|
(models.DateTimeField, (openapi.TYPE_STRING, openapi.FORMAT_DATETIME)),
|
||||||
(models.DateField, (openapi.TYPE_STRING, openapi.FORMAT_DATE)),
|
(models.DateField, (openapi.TYPE_STRING, openapi.FORMAT_DATE)),
|
||||||
(models.DecimalField, (openapi.TYPE_NUMBER, None)),
|
(models.DecimalField, (decimal_field_type, openapi.FORMAT_DECIMAL)),
|
||||||
(models.DurationField, (openapi.TYPE_INTEGER, None)),
|
(models.DurationField, (openapi.TYPE_INTEGER, None)),
|
||||||
(models.FloatField, (openapi.TYPE_NUMBER, None)),
|
(models.FloatField, (openapi.TYPE_NUMBER, None)),
|
||||||
(models.IntegerField, (openapi.TYPE_INTEGER, None)),
|
(models.IntegerField, (openapi.TYPE_INTEGER, None)),
|
||||||
@@ -300,9 +312,11 @@ serializer_field_to_basic_type = [
|
|||||||
(serializers.UUIDField, (openapi.TYPE_STRING, openapi.FORMAT_UUID)),
|
(serializers.UUIDField, (openapi.TYPE_STRING, openapi.FORMAT_UUID)),
|
||||||
(serializers.RegexField, (openapi.TYPE_STRING, None)),
|
(serializers.RegexField, (openapi.TYPE_STRING, None)),
|
||||||
(serializers.CharField, (openapi.TYPE_STRING, None)),
|
(serializers.CharField, (openapi.TYPE_STRING, None)),
|
||||||
((serializers.BooleanField, serializers.NullBooleanField), (openapi.TYPE_BOOLEAN, None)),
|
(serializers.BooleanField, (openapi.TYPE_BOOLEAN, None)),
|
||||||
|
(serializers.NullBooleanField, (openapi.TYPE_BOOLEAN, None)),
|
||||||
(serializers.IntegerField, (openapi.TYPE_INTEGER, None)),
|
(serializers.IntegerField, (openapi.TYPE_INTEGER, None)),
|
||||||
((serializers.FloatField, serializers.DecimalField), (openapi.TYPE_NUMBER, None)),
|
(serializers.FloatField, (openapi.TYPE_NUMBER, None)),
|
||||||
|
(serializers.DecimalField, (decimal_field_type, openapi.FORMAT_DECIMAL)),
|
||||||
(serializers.DurationField, (openapi.TYPE_NUMBER, None)), # ?
|
(serializers.DurationField, (openapi.TYPE_NUMBER, None)), # ?
|
||||||
(serializers.DateField, (openapi.TYPE_STRING, openapi.FORMAT_DATE)),
|
(serializers.DateField, (openapi.TYPE_STRING, openapi.FORMAT_DATE)),
|
||||||
(serializers.DateTimeField, (openapi.TYPE_STRING, openapi.FORMAT_DATETIME)),
|
(serializers.DateTimeField, (openapi.TYPE_STRING, openapi.FORMAT_DATETIME)),
|
||||||
@@ -326,6 +340,8 @@ def get_basic_type_info(field):
|
|||||||
for field_class, type_format in basic_type_info:
|
for field_class, type_format in basic_type_info:
|
||||||
if isinstance(field, field_class):
|
if isinstance(field, field_class):
|
||||||
swagger_type, format = type_format
|
swagger_type, format = type_format
|
||||||
|
if callable(swagger_type):
|
||||||
|
swagger_type = swagger_type(field)
|
||||||
if callable(format):
|
if callable(format):
|
||||||
format = format(field)
|
format = format(field)
|
||||||
break
|
break
|
||||||
|
|||||||
@@ -6,7 +6,10 @@ from rest_framework.status import is_success
|
|||||||
|
|
||||||
from .. import openapi
|
from .. import openapi
|
||||||
from ..errors import SwaggerGenerationError
|
from ..errors import SwaggerGenerationError
|
||||||
from ..utils import force_serializer_instance, guess_response_status, is_list_view, no_body, param_list_to_odict
|
from ..utils import (
|
||||||
|
force_serializer_instance, get_consumes, get_produces, guess_response_status, is_list_view, no_body,
|
||||||
|
param_list_to_odict
|
||||||
|
)
|
||||||
from .base import ViewInspector
|
from .base import ViewInspector
|
||||||
|
|
||||||
|
|
||||||
@@ -18,6 +21,7 @@ class SwaggerAutoSchema(ViewInspector):
|
|||||||
|
|
||||||
def get_operation(self, operation_keys):
|
def get_operation(self, operation_keys):
|
||||||
consumes = self.get_consumes()
|
consumes = self.get_consumes()
|
||||||
|
produces = self.get_produces()
|
||||||
|
|
||||||
body = self.get_request_body_parameters(consumes)
|
body = self.get_request_body_parameters(consumes)
|
||||||
query = self.get_query_parameters()
|
query = self.get_query_parameters()
|
||||||
@@ -39,6 +43,7 @@ class SwaggerAutoSchema(ViewInspector):
|
|||||||
responses=responses,
|
responses=responses,
|
||||||
parameters=parameters,
|
parameters=parameters,
|
||||||
consumes=consumes,
|
consumes=consumes,
|
||||||
|
produces=produces,
|
||||||
tags=tags,
|
tags=tags,
|
||||||
security=security
|
security=security
|
||||||
)
|
)
|
||||||
@@ -91,8 +96,8 @@ class SwaggerAutoSchema(ViewInspector):
|
|||||||
if body_override is no_body:
|
if body_override is no_body:
|
||||||
return None
|
return None
|
||||||
if self.method not in self.body_methods:
|
if self.method not in self.body_methods:
|
||||||
raise SwaggerGenerationError("request_body can only be applied to PUT, PATCH or POST views; "
|
raise SwaggerGenerationError("request_body can only be applied to (" + ','.join(self.body_methods) +
|
||||||
"are you looking for query_serializer or manual_parameters?")
|
"); are you looking for query_serializer or manual_parameters?")
|
||||||
if isinstance(body_override, openapi.Schema.OR_REF):
|
if isinstance(body_override, openapi.Schema.OR_REF):
|
||||||
return body_override
|
return body_override
|
||||||
return force_serializer_instance(body_override)
|
return force_serializer_instance(body_override)
|
||||||
@@ -296,7 +301,7 @@ class SwaggerAutoSchema(ViewInspector):
|
|||||||
authentication schemes). Returning ``None`` will inherit the top-level secuirty requirements.
|
authentication schemes). Returning ``None`` will inherit the top-level secuirty requirements.
|
||||||
|
|
||||||
:return: security requirements
|
:return: security requirements
|
||||||
:rtype: list"""
|
:rtype: list[dict[str,list[str]]]"""
|
||||||
return self.overrides.get('security', None)
|
return self.overrides.get('security', None)
|
||||||
|
|
||||||
def get_tags(self, operation_keys):
|
def get_tags(self, operation_keys):
|
||||||
@@ -314,7 +319,11 @@ class SwaggerAutoSchema(ViewInspector):
|
|||||||
|
|
||||||
:rtype: list[str]
|
:rtype: list[str]
|
||||||
"""
|
"""
|
||||||
media_types = [parser.media_type for parser in getattr(self.view, 'parser_classes', [])]
|
return get_consumes(getattr(self.view, 'parser_classes', []))
|
||||||
if all(is_form_media_type(encoding) for encoding in media_types):
|
|
||||||
return media_types
|
def get_produces(self):
|
||||||
return media_types[:1]
|
"""Return the MIME types this endpoint can produce.
|
||||||
|
|
||||||
|
:rtype: list[str]
|
||||||
|
"""
|
||||||
|
return get_produces(getattr(self.view, 'renderer_classes', []))
|
||||||
|
|||||||
+15
-3
@@ -35,6 +35,7 @@ FORMAT_URI = "uri" #:
|
|||||||
# pulled out of my ass
|
# pulled out of my ass
|
||||||
FORMAT_UUID = "uuid" #:
|
FORMAT_UUID = "uuid" #:
|
||||||
FORMAT_SLUG = "slug" #:
|
FORMAT_SLUG = "slug" #:
|
||||||
|
FORMAT_DECIMAL = "decimal"
|
||||||
|
|
||||||
IN_BODY = 'body' #:
|
IN_BODY = 'body' #:
|
||||||
IN_PATH = 'path' #:
|
IN_PATH = 'path' #:
|
||||||
@@ -211,7 +212,8 @@ class Info(SwaggerDict):
|
|||||||
|
|
||||||
|
|
||||||
class Swagger(SwaggerDict):
|
class Swagger(SwaggerDict):
|
||||||
def __init__(self, info=None, _url=None, _prefix=None, _version=None, paths=None, definitions=None, **extra):
|
def __init__(self, info=None, _url=None, _prefix=None, _version=None, consumes=None, produces=None,
|
||||||
|
security_definitions=None, security=None, paths=None, definitions=None, **extra):
|
||||||
"""Root Swagger object.
|
"""Root Swagger object.
|
||||||
|
|
||||||
:param .Info info: info object
|
:param .Info info: info object
|
||||||
@@ -219,6 +221,10 @@ class Swagger(SwaggerDict):
|
|||||||
:param str _prefix: api path prefix to use in setting basePath; this will be appended to the wsgi
|
:param str _prefix: api path prefix to use in setting basePath; this will be appended to the wsgi
|
||||||
SCRIPT_NAME prefix or Django's FORCE_SCRIPT_NAME if applicable
|
SCRIPT_NAME prefix or Django's FORCE_SCRIPT_NAME if applicable
|
||||||
:param str _version: version string to override Info
|
:param str _version: version string to override Info
|
||||||
|
:param list[dict] security_definitions: list of supported authentication mechanisms
|
||||||
|
:param list[dict] security: authentication mechanisms accepted by default; can be overriden in Operation
|
||||||
|
:param list[str] consumes: consumed MIME types; can be overriden in Operation
|
||||||
|
:param list[str] produces: produced MIME types; can be overriden in Operation
|
||||||
:param .Paths paths: paths object
|
:param .Paths paths: paths object
|
||||||
:param dict[str,.Schema] definitions: named models
|
:param dict[str,.Schema] definitions: named models
|
||||||
"""
|
"""
|
||||||
@@ -234,6 +240,10 @@ class Swagger(SwaggerDict):
|
|||||||
self.schemes = [url.scheme]
|
self.schemes = [url.scheme]
|
||||||
|
|
||||||
self.base_path = self.get_base_path(get_script_prefix(), _prefix)
|
self.base_path = self.get_base_path(get_script_prefix(), _prefix)
|
||||||
|
self.consumes = consumes
|
||||||
|
self.produces = produces
|
||||||
|
self.security_definitions = filter_none(security_definitions)
|
||||||
|
self.security = filter_none(security)
|
||||||
self.paths = paths
|
self.paths = paths
|
||||||
self.definitions = filter_none(definitions)
|
self.definitions = filter_none(definitions)
|
||||||
self._insert_extras__()
|
self._insert_extras__()
|
||||||
@@ -304,8 +314,8 @@ class PathItem(SwaggerDict):
|
|||||||
|
|
||||||
|
|
||||||
class Operation(SwaggerDict):
|
class Operation(SwaggerDict):
|
||||||
def __init__(self, operation_id, responses, parameters=None, consumes=None,
|
def __init__(self, operation_id, responses, parameters=None, consumes=None, produces=None, summary=None,
|
||||||
produces=None, summary=None, description=None, tags=None, **extra):
|
description=None, tags=None, security=None, **extra):
|
||||||
"""Information about an API operation (path + http method combination)
|
"""Information about an API operation (path + http method combination)
|
||||||
|
|
||||||
:param str operation_id: operation ID, should be unique across all operations
|
:param str operation_id: operation ID, should be unique across all operations
|
||||||
@@ -316,6 +326,7 @@ class Operation(SwaggerDict):
|
|||||||
:param str summary: operation summary; should be < 120 characters
|
:param str summary: operation summary; should be < 120 characters
|
||||||
:param str description: operation description; can be of any length and supports markdown
|
:param str description: operation description; can be of any length and supports markdown
|
||||||
:param list[str] tags: operation tags
|
:param list[str] tags: operation tags
|
||||||
|
:param list[dict[str,list[str]]] security: list of security requirements
|
||||||
"""
|
"""
|
||||||
super(Operation, self).__init__(**extra)
|
super(Operation, self).__init__(**extra)
|
||||||
self.operation_id = operation_id
|
self.operation_id = operation_id
|
||||||
@@ -326,6 +337,7 @@ class Operation(SwaggerDict):
|
|||||||
self.consumes = filter_none(consumes)
|
self.consumes = filter_none(consumes)
|
||||||
self.produces = filter_none(produces)
|
self.produces = filter_none(produces)
|
||||||
self.tags = filter_none(tags)
|
self.tags = filter_none(tags)
|
||||||
|
self.security = filter_none(security)
|
||||||
self._insert_extras__()
|
self._insert_extras__()
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
from django.shortcuts import render, resolve_url
|
from django.shortcuts import render, resolve_url
|
||||||
from rest_framework.renderers import BaseRenderer
|
from rest_framework.renderers import BaseRenderer, TemplateHTMLRenderer
|
||||||
from rest_framework.utils import json
|
from rest_framework.utils import json
|
||||||
|
|
||||||
|
from drf_yasg.openapi import Swagger
|
||||||
|
|
||||||
from .app_settings import redoc_settings, swagger_settings
|
from .app_settings import redoc_settings, swagger_settings
|
||||||
from .codecs import VALIDATORS, OpenAPICodecJson, OpenAPICodecYaml
|
from .codecs import VALIDATORS, OpenAPICodecJson, OpenAPICodecYaml
|
||||||
|
|
||||||
@@ -51,6 +53,11 @@ class _UIRenderer(BaseRenderer):
|
|||||||
template = ''
|
template = ''
|
||||||
|
|
||||||
def render(self, swagger, accepted_media_type=None, renderer_context=None):
|
def render(self, swagger, accepted_media_type=None, renderer_context=None):
|
||||||
|
if not isinstance(swagger, Swagger):
|
||||||
|
# if `swagger` is not a ``Swagger`` object, it means we somehow got a non-success ``Response``
|
||||||
|
# in that case, it's probably better to let the default ``TemplateHTMLRenderer`` render it
|
||||||
|
# see https://github.com/axnsan12/drf-yasg/issues/58
|
||||||
|
return TemplateHTMLRenderer().render(swagger, accepted_media_type, renderer_context)
|
||||||
self.set_context(renderer_context, swagger)
|
self.set_context(renderer_context, swagger)
|
||||||
return render(
|
return render(
|
||||||
renderer_context['request'],
|
renderer_context['request'],
|
||||||
@@ -63,6 +70,7 @@ class _UIRenderer(BaseRenderer):
|
|||||||
renderer_context['version'] = swagger.info.version
|
renderer_context['version'] = swagger.info.version
|
||||||
renderer_context['swagger_settings'] = json.dumps(self.get_swagger_ui_settings())
|
renderer_context['swagger_settings'] = json.dumps(self.get_swagger_ui_settings())
|
||||||
renderer_context['redoc_settings'] = json.dumps(self.get_redoc_settings())
|
renderer_context['redoc_settings'] = json.dumps(self.get_redoc_settings())
|
||||||
|
renderer_context['oauth2_config'] = json.dumps(self.get_oauth2_config())
|
||||||
renderer_context['USE_SESSION_AUTH'] = swagger_settings.USE_SESSION_AUTH
|
renderer_context['USE_SESSION_AUTH'] = swagger_settings.USE_SESSION_AUTH
|
||||||
renderer_context.update(self.get_auth_urls())
|
renderer_context.update(self.get_auth_urls())
|
||||||
|
|
||||||
@@ -85,6 +93,7 @@ class _UIRenderer(BaseRenderer):
|
|||||||
'defaultModelRendering': swagger_settings.DEFAULT_MODEL_RENDERING,
|
'defaultModelRendering': swagger_settings.DEFAULT_MODEL_RENDERING,
|
||||||
'defaultModelExpandDepth': swagger_settings.DEFAULT_MODEL_DEPTH,
|
'defaultModelExpandDepth': swagger_settings.DEFAULT_MODEL_DEPTH,
|
||||||
'defaultModelsExpandDepth': swagger_settings.DEFAULT_MODEL_DEPTH,
|
'defaultModelsExpandDepth': swagger_settings.DEFAULT_MODEL_DEPTH,
|
||||||
|
'oauth2RedirectUrl': swagger_settings.OAUTH2_REDIRECT_URL,
|
||||||
}
|
}
|
||||||
data = {k: v for k, v in data.items() if v is not None}
|
data = {k: v for k, v in data.items() if v is not None}
|
||||||
if swagger_settings.VALIDATOR_URL != '':
|
if swagger_settings.VALIDATOR_URL != '':
|
||||||
@@ -102,6 +111,11 @@ class _UIRenderer(BaseRenderer):
|
|||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
def get_oauth2_config(self):
|
||||||
|
data = swagger_settings.OAUTH2_CONFIG
|
||||||
|
assert isinstance(data, dict), "OAUTH2_CONFIG must be a dict"
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
class SwaggerUIRenderer(_UIRenderer):
|
class SwaggerUIRenderer(_UIRenderer):
|
||||||
"""Renders a swagger-ui web interface for schema browisng.
|
"""Renders a swagger-ui web interface for schema browisng.
|
||||||
|
|||||||
+2
-2
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -49,6 +49,7 @@ function initSwaggerUi() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
var swaggerSettings = JSON.parse(document.getElementById('swagger-settings').innerHTML);
|
var swaggerSettings = JSON.parse(document.getElementById('swagger-settings').innerHTML);
|
||||||
|
console.log(swaggerSettings);
|
||||||
|
|
||||||
for (var p in swaggerSettings) {
|
for (var p in swaggerSettings) {
|
||||||
if (swaggerSettings.hasOwnProperty(p)) {
|
if (swaggerSettings.hasOwnProperty(p)) {
|
||||||
@@ -56,6 +57,10 @@ function initSwaggerUi() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
window.ui = SwaggerUIBundle(swaggerConfig);
|
window.ui = SwaggerUIBundle(swaggerConfig);
|
||||||
|
|
||||||
|
var oauth2Config = JSON.parse(document.getElementById('oauth2-config').innerHTML);
|
||||||
|
console.log(oauth2Config);
|
||||||
|
window.ui.initOAuth(oauth2Config);
|
||||||
}
|
}
|
||||||
|
|
||||||
window.onload = function () {
|
window.onload = function () {
|
||||||
|
|||||||
@@ -60,6 +60,7 @@
|
|||||||
|
|
||||||
|
|
||||||
<script id="swagger-settings" type="application/json">{{ swagger_settings | safe }}</script>
|
<script id="swagger-settings" type="application/json">{{ swagger_settings | safe }}</script>
|
||||||
|
<script id="oauth2-config" type="application/json">{{ oauth2_config | safe }}</script>
|
||||||
|
|
||||||
<script src="{% static 'drf-yasg/swagger-ui-dist/swagger-ui-bundle.js' %}"></script>
|
<script src="{% static 'drf-yasg/swagger-ui-dist/swagger-ui-bundle.js' %}"></script>
|
||||||
<script src="{% static 'drf-yasg/swagger-ui-dist/swagger-ui-standalone-preset.js' %}"></script>
|
<script src="{% static 'drf-yasg/swagger-ui-dist/swagger-ui-standalone-preset.js' %}"></script>
|
||||||
|
|||||||
+50
-9
@@ -2,8 +2,11 @@ import inspect
|
|||||||
import logging
|
import logging
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
|
|
||||||
|
from django.db import models
|
||||||
from rest_framework import serializers, status
|
from rest_framework import serializers, status
|
||||||
from rest_framework.mixins import DestroyModelMixin, RetrieveModelMixin, UpdateModelMixin
|
from rest_framework.mixins import DestroyModelMixin, RetrieveModelMixin, UpdateModelMixin
|
||||||
|
from rest_framework.request import is_form_media_type
|
||||||
|
from rest_framework.settings import api_settings as rest_framework_settings
|
||||||
from rest_framework.views import APIView
|
from rest_framework.views import APIView
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@@ -11,8 +14,10 @@ logger = logging.getLogger(__name__)
|
|||||||
#: used to forcibly remove the body of a request via :func:`.swagger_auto_schema`
|
#: used to forcibly remove the body of a request via :func:`.swagger_auto_schema`
|
||||||
no_body = object()
|
no_body = object()
|
||||||
|
|
||||||
|
unset = object()
|
||||||
|
|
||||||
def swagger_auto_schema(method=None, methods=None, auto_schema=None, request_body=None, query_serializer=None,
|
|
||||||
|
def swagger_auto_schema(method=None, methods=None, auto_schema=unset, request_body=None, query_serializer=None,
|
||||||
manual_parameters=None, operation_id=None, operation_description=None, security=None,
|
manual_parameters=None, operation_id=None, operation_description=None, security=None,
|
||||||
responses=None, field_inspectors=None, filter_inspectors=None, paginator_inspectors=None,
|
responses=None, field_inspectors=None, filter_inspectors=None, paginator_inspectors=None,
|
||||||
**extra_overrides):
|
**extra_overrides):
|
||||||
@@ -23,17 +28,11 @@ def swagger_auto_schema(method=None, methods=None, auto_schema=None, request_bod
|
|||||||
|
|
||||||
The `auto_schema` and `operation_description` arguments take precendence over view- or method-level values.
|
The `auto_schema` and `operation_description` arguments take precendence over view- or method-level values.
|
||||||
|
|
||||||
.. versionchanged:: 1.1
|
|
||||||
Added the ``extra_overrides`` and ``operatiod_id`` parameters.
|
|
||||||
|
|
||||||
.. versionchanged:: 1.1
|
|
||||||
Added the ``field_inspectors``, ``filter_inspectors`` and ``paginator_inspectors`` parameters.
|
|
||||||
|
|
||||||
:param str method: for multi-method views, the http method the options should apply to
|
:param str method: for multi-method views, the http method the options should apply to
|
||||||
:param list[str] methods: for multi-method views, the http methods the options should apply to
|
:param list[str] methods: for multi-method views, the http methods the options should apply to
|
||||||
:param .inspectors.SwaggerAutoSchema auto_schema: custom class to use for generating the Operation object;
|
:param .inspectors.SwaggerAutoSchema auto_schema: custom class to use for generating the Operation object;
|
||||||
this overrides both the class-level ``swagger_schema`` attribute and the ``DEFAULT_AUTO_SCHEMA_CLASS``
|
this overrides both the class-level ``swagger_schema`` attribute and the ``DEFAULT_AUTO_SCHEMA_CLASS``
|
||||||
setting
|
setting, and can be set to ``None`` to prevent this operation from being generated
|
||||||
:param .Schema,.SchemaRef,.Serializer request_body: custom request body, or :data:`.no_body`. The value given here
|
:param .Schema,.SchemaRef,.Serializer request_body: custom request body, or :data:`.no_body`. The value given here
|
||||||
will be used as the ``schema`` property of a :class:`.Parameter` with ``in: 'body'``.
|
will be used as the ``schema`` property of a :class:`.Parameter` with ``in: 'body'``.
|
||||||
|
|
||||||
@@ -91,7 +90,6 @@ def swagger_auto_schema(method=None, methods=None, auto_schema=None, request_bod
|
|||||||
def decorator(view_method):
|
def decorator(view_method):
|
||||||
assert not any(hm in extra_overrides for hm in APIView.http_method_names), "HTTP method names not allowed here"
|
assert not any(hm in extra_overrides for hm in APIView.http_method_names), "HTTP method names not allowed here"
|
||||||
data = {
|
data = {
|
||||||
'auto_schema': auto_schema,
|
|
||||||
'request_body': request_body,
|
'request_body': request_body,
|
||||||
'query_serializer': query_serializer,
|
'query_serializer': query_serializer,
|
||||||
'manual_parameters': manual_parameters,
|
'manual_parameters': manual_parameters,
|
||||||
@@ -104,6 +102,8 @@ def swagger_auto_schema(method=None, methods=None, auto_schema=None, request_bod
|
|||||||
'field_inspectors': list(field_inspectors) if field_inspectors else None,
|
'field_inspectors': list(field_inspectors) if field_inspectors else None,
|
||||||
}
|
}
|
||||||
data = filter_none(data)
|
data = filter_none(data)
|
||||||
|
if auto_schema is not unset:
|
||||||
|
data['auto_schema'] = auto_schema
|
||||||
data.update(extra_overrides)
|
data.update(extra_overrides)
|
||||||
if not data: # pragma: no cover
|
if not data: # pragma: no cover
|
||||||
# no overrides to set, no use in doing more work
|
# no overrides to set, no use in doing more work
|
||||||
@@ -120,6 +120,8 @@ def swagger_auto_schema(method=None, methods=None, auto_schema=None, request_bod
|
|||||||
|
|
||||||
_methods = methods
|
_methods = methods
|
||||||
if methods or method:
|
if methods or method:
|
||||||
|
assert available_methods or http_method_names, "`method` or `methods` can only be specified " \
|
||||||
|
"on @detail_route or @api_view views"
|
||||||
assert bool(methods) != bool(method), "specify either method or methods"
|
assert bool(methods) != bool(method), "specify either method or methods"
|
||||||
assert not isinstance(methods, str), "`methods` expects to receive a list of methods;" \
|
assert not isinstance(methods, str), "`methods` expects to receive a list of methods;" \
|
||||||
" use `method` for a single argument"
|
" use `method` for a single argument"
|
||||||
@@ -248,3 +250,42 @@ def force_serializer_instance(serializer):
|
|||||||
assert isinstance(serializer, serializers.BaseSerializer), \
|
assert isinstance(serializer, serializers.BaseSerializer), \
|
||||||
"Serializer class or instance required, not %s" % type(serializer).__name__
|
"Serializer class or instance required, not %s" % type(serializer).__name__
|
||||||
return serializer
|
return serializer
|
||||||
|
|
||||||
|
|
||||||
|
def get_consumes(parser_classes):
|
||||||
|
"""Extract ``consumes`` MIME types from a list of parser classes.
|
||||||
|
|
||||||
|
:param list parser_classes: parser classes
|
||||||
|
:return: MIME types for ``consumes``
|
||||||
|
:rtype: list[str]
|
||||||
|
"""
|
||||||
|
media_types = [parser.media_type for parser in parser_classes or []]
|
||||||
|
if all(is_form_media_type(encoding) for encoding in media_types):
|
||||||
|
return media_types
|
||||||
|
else:
|
||||||
|
media_types = [encoding for encoding in media_types if not is_form_media_type(encoding)]
|
||||||
|
return media_types
|
||||||
|
|
||||||
|
|
||||||
|
def get_produces(renderer_classes):
|
||||||
|
"""Extract ``produces`` MIME types from a list of renderer classes.
|
||||||
|
|
||||||
|
:param list renderer_classes: renderer classes
|
||||||
|
:return: MIME types for ``produces``
|
||||||
|
:rtype: list[str]
|
||||||
|
"""
|
||||||
|
media_types = [renderer.media_type for renderer in renderer_classes or []]
|
||||||
|
media_types = [encoding for encoding in media_types if 'html' not in encoding]
|
||||||
|
return media_types
|
||||||
|
|
||||||
|
|
||||||
|
def decimal_as_float(field):
|
||||||
|
"""
|
||||||
|
Returns true if ``field`` is a django-rest-framework DecimalField and its ``coerce_to_string`` attribute or the
|
||||||
|
``COERCE_DECIMAL_TO_STRING`` setting is set to ``False``.
|
||||||
|
|
||||||
|
:rtype: bool
|
||||||
|
"""
|
||||||
|
if isinstance(field, serializers.DecimalField) or isinstance(field, models.DecimalField):
|
||||||
|
return not getattr(field, 'coerce_to_string', rest_framework_settings.COERCE_DECIMAL_TO_STRING)
|
||||||
|
return False
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
|
from decimal import Decimal
|
||||||
|
|
||||||
from django.contrib.auth import get_user_model
|
from django.contrib.auth import get_user_model
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
from rest_framework.compat import MinValueValidator
|
||||||
|
|
||||||
from snippets.models import LANGUAGE_CHOICES, STYLE_CHOICES, Snippet
|
from snippets.models import LANGUAGE_CHOICES, STYLE_CHOICES, Snippet
|
||||||
|
|
||||||
|
|
||||||
class LanguageSerializer(serializers.Serializer):
|
class LanguageSerializer(serializers.Serializer):
|
||||||
|
|
||||||
name = serializers.ChoiceField(
|
name = serializers.ChoiceField(
|
||||||
choices=LANGUAGE_CHOICES, default='python', help_text='The name of the programming language')
|
choices=LANGUAGE_CHOICES, default='python', help_text='The name of the programming language')
|
||||||
|
|
||||||
@@ -14,7 +16,6 @@ class LanguageSerializer(serializers.Serializer):
|
|||||||
|
|
||||||
|
|
||||||
class ExampleProjectSerializer(serializers.Serializer):
|
class ExampleProjectSerializer(serializers.Serializer):
|
||||||
|
|
||||||
project_name = serializers.CharField(help_text='Name of the project')
|
project_name = serializers.CharField(help_text='Name of the project')
|
||||||
github_repo = serializers.CharField(required=True, help_text='Github repository of the project')
|
github_repo = serializers.CharField(required=True, help_text='Github repository of the project')
|
||||||
|
|
||||||
@@ -49,6 +50,10 @@ class SnippetSerializer(serializers.Serializer):
|
|||||||
example_projects = serializers.ListSerializer(child=ExampleProjectSerializer(), read_only=True)
|
example_projects = serializers.ListSerializer(child=ExampleProjectSerializer(), read_only=True)
|
||||||
difficulty_factor = serializers.FloatField(help_text="this is here just to test FloatField",
|
difficulty_factor = serializers.FloatField(help_text="this is here just to test FloatField",
|
||||||
read_only=True, default=lambda: 6.9)
|
read_only=True, default=lambda: 6.9)
|
||||||
|
rate_as_string = serializers.DecimalField(max_digits=6, decimal_places=3, default=Decimal('0.0'),
|
||||||
|
validators=[MinValueValidator(Decimal('0.0'))])
|
||||||
|
rate = serializers.DecimalField(max_digits=6, decimal_places=3, default=Decimal('0.0'), coerce_to_string=False,
|
||||||
|
validators=[MinValueValidator(Decimal('0.0'))])
|
||||||
|
|
||||||
def create(self, validated_data):
|
def create(self, validated_data):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ from djangorestframework_camel_case.parser import CamelCaseJSONParser
|
|||||||
from djangorestframework_camel_case.render import CamelCaseJSONRenderer
|
from djangorestframework_camel_case.render import CamelCaseJSONRenderer
|
||||||
from inflection import camelize
|
from inflection import camelize
|
||||||
from rest_framework import generics
|
from rest_framework import generics
|
||||||
|
from rest_framework.parsers import FormParser
|
||||||
|
|
||||||
from drf_yasg import openapi
|
from drf_yasg import openapi
|
||||||
from drf_yasg.inspectors import SwaggerAutoSchema
|
from drf_yasg.inspectors import SwaggerAutoSchema
|
||||||
@@ -21,7 +22,7 @@ class SnippetList(generics.ListCreateAPIView):
|
|||||||
queryset = Snippet.objects.all()
|
queryset = Snippet.objects.all()
|
||||||
serializer_class = SnippetSerializer
|
serializer_class = SnippetSerializer
|
||||||
|
|
||||||
parser_classes = (CamelCaseJSONParser,)
|
parser_classes = (FormParser, CamelCaseJSONParser,)
|
||||||
renderer_classes = (CamelCaseJSONRenderer,)
|
renderer_classes = (CamelCaseJSONRenderer,)
|
||||||
swagger_schema = CamelCaseOperationIDAutoSchema
|
swagger_schema = CamelCaseOperationIDAutoSchema
|
||||||
|
|
||||||
|
|||||||
+20
-43
@@ -16,6 +16,15 @@ host: test.local:8002
|
|||||||
schemes:
|
schemes:
|
||||||
- http
|
- http
|
||||||
basePath: /
|
basePath: /
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
securityDefinitions:
|
||||||
|
basic:
|
||||||
|
type: basic
|
||||||
|
security:
|
||||||
|
- basic: []
|
||||||
paths:
|
paths:
|
||||||
/articles/:
|
/articles/:
|
||||||
get:
|
get:
|
||||||
@@ -63,8 +72,6 @@ paths:
|
|||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
$ref: '#/definitions/Article'
|
$ref: '#/definitions/Article'
|
||||||
consumes:
|
|
||||||
- application/json
|
|
||||||
tags:
|
tags:
|
||||||
- articles
|
- articles
|
||||||
post:
|
post:
|
||||||
@@ -81,8 +88,6 @@ paths:
|
|||||||
description: ''
|
description: ''
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/definitions/Article'
|
$ref: '#/definitions/Article'
|
||||||
consumes:
|
|
||||||
- application/json
|
|
||||||
tags:
|
tags:
|
||||||
- articles
|
- articles
|
||||||
parameters: []
|
parameters: []
|
||||||
@@ -108,8 +113,6 @@ paths:
|
|||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
$ref: '#/definitions/Article'
|
$ref: '#/definitions/Article'
|
||||||
consumes:
|
|
||||||
- application/json
|
|
||||||
tags:
|
tags:
|
||||||
- articles
|
- articles
|
||||||
parameters: []
|
parameters: []
|
||||||
@@ -123,8 +126,6 @@ paths:
|
|||||||
description: ''
|
description: ''
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/definitions/Article'
|
$ref: '#/definitions/Article'
|
||||||
consumes:
|
|
||||||
- application/json
|
|
||||||
tags:
|
tags:
|
||||||
- articles
|
- articles
|
||||||
put:
|
put:
|
||||||
@@ -141,8 +142,6 @@ paths:
|
|||||||
description: ''
|
description: ''
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/definitions/Article'
|
$ref: '#/definitions/Article'
|
||||||
consumes:
|
|
||||||
- application/json
|
|
||||||
tags:
|
tags:
|
||||||
- articles
|
- articles
|
||||||
patch:
|
patch:
|
||||||
@@ -161,8 +160,6 @@ paths:
|
|||||||
$ref: '#/definitions/Article'
|
$ref: '#/definitions/Article'
|
||||||
'404':
|
'404':
|
||||||
description: slug not found
|
description: slug not found
|
||||||
consumes:
|
|
||||||
- application/json
|
|
||||||
tags:
|
tags:
|
||||||
- articles
|
- articles
|
||||||
delete:
|
delete:
|
||||||
@@ -172,8 +169,6 @@ paths:
|
|||||||
responses:
|
responses:
|
||||||
'204':
|
'204':
|
||||||
description: ''
|
description: ''
|
||||||
consumes:
|
|
||||||
- application/json
|
|
||||||
tags:
|
tags:
|
||||||
- articles
|
- articles
|
||||||
parameters:
|
parameters:
|
||||||
@@ -246,8 +241,6 @@ paths:
|
|||||||
responses:
|
responses:
|
||||||
'200':
|
'200':
|
||||||
description: ''
|
description: ''
|
||||||
consumes:
|
|
||||||
- application/json
|
|
||||||
tags:
|
tags:
|
||||||
- plain
|
- plain
|
||||||
parameters: []
|
parameters: []
|
||||||
@@ -263,8 +256,6 @@ paths:
|
|||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
$ref: '#/definitions/Snippet'
|
$ref: '#/definitions/Snippet'
|
||||||
consumes:
|
|
||||||
- application/json
|
|
||||||
tags:
|
tags:
|
||||||
- snippets
|
- snippets
|
||||||
post:
|
post:
|
||||||
@@ -281,8 +272,6 @@ paths:
|
|||||||
description: ''
|
description: ''
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/definitions/Snippet'
|
$ref: '#/definitions/Snippet'
|
||||||
consumes:
|
|
||||||
- application/json
|
|
||||||
tags:
|
tags:
|
||||||
- snippets
|
- snippets
|
||||||
parameters: []
|
parameters: []
|
||||||
@@ -296,8 +285,6 @@ paths:
|
|||||||
description: ''
|
description: ''
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/definitions/Snippet'
|
$ref: '#/definitions/Snippet'
|
||||||
consumes:
|
|
||||||
- application/json
|
|
||||||
tags:
|
tags:
|
||||||
- snippets
|
- snippets
|
||||||
put:
|
put:
|
||||||
@@ -314,8 +301,6 @@ paths:
|
|||||||
description: ''
|
description: ''
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/definitions/Snippet'
|
$ref: '#/definitions/Snippet'
|
||||||
consumes:
|
|
||||||
- application/json
|
|
||||||
tags:
|
tags:
|
||||||
- snippets
|
- snippets
|
||||||
patch:
|
patch:
|
||||||
@@ -332,8 +317,6 @@ paths:
|
|||||||
description: ''
|
description: ''
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/definitions/Snippet'
|
$ref: '#/definitions/Snippet'
|
||||||
consumes:
|
|
||||||
- application/json
|
|
||||||
tags:
|
tags:
|
||||||
- snippets
|
- snippets
|
||||||
delete:
|
delete:
|
||||||
@@ -348,8 +331,6 @@ paths:
|
|||||||
responses:
|
responses:
|
||||||
'204':
|
'204':
|
||||||
description: ''
|
description: ''
|
||||||
consumes:
|
|
||||||
- application/json
|
|
||||||
tags:
|
tags:
|
||||||
- snippets
|
- snippets
|
||||||
parameters:
|
parameters:
|
||||||
@@ -380,8 +361,6 @@ paths:
|
|||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
$ref: '#/definitions/UserSerializerrr'
|
$ref: '#/definitions/UserSerializerrr'
|
||||||
consumes:
|
|
||||||
- application/json
|
|
||||||
tags:
|
tags:
|
||||||
- users
|
- users
|
||||||
post:
|
post:
|
||||||
@@ -408,8 +387,6 @@ paths:
|
|||||||
properties:
|
properties:
|
||||||
username:
|
username:
|
||||||
type: string
|
type: string
|
||||||
consumes:
|
|
||||||
- application/json
|
|
||||||
tags:
|
tags:
|
||||||
- users
|
- users
|
||||||
security: []
|
security: []
|
||||||
@@ -420,8 +397,6 @@ paths:
|
|||||||
responses:
|
responses:
|
||||||
'200':
|
'200':
|
||||||
description: ''
|
description: ''
|
||||||
consumes:
|
|
||||||
- application/json
|
|
||||||
tags:
|
tags:
|
||||||
- users
|
- users
|
||||||
parameters: []
|
parameters: []
|
||||||
@@ -439,8 +414,6 @@ paths:
|
|||||||
description: response description
|
description: response description
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/definitions/UserSerializerrr'
|
$ref: '#/definitions/UserSerializerrr'
|
||||||
consumes:
|
|
||||||
- application/json
|
|
||||||
tags:
|
tags:
|
||||||
- users
|
- users
|
||||||
put:
|
put:
|
||||||
@@ -457,8 +430,6 @@ paths:
|
|||||||
description: ''
|
description: ''
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/definitions/UserSerializerrr'
|
$ref: '#/definitions/UserSerializerrr'
|
||||||
consumes:
|
|
||||||
- application/json
|
|
||||||
tags:
|
tags:
|
||||||
- users
|
- users
|
||||||
parameters:
|
parameters:
|
||||||
@@ -1065,6 +1036,17 @@ definitions:
|
|||||||
type: number
|
type: number
|
||||||
readOnly: true
|
readOnly: true
|
||||||
default: 6.9
|
default: 6.9
|
||||||
|
rateAsString:
|
||||||
|
title: Rate as string
|
||||||
|
type: string
|
||||||
|
format: decimal
|
||||||
|
default: '0.000'
|
||||||
|
rate:
|
||||||
|
title: Rate
|
||||||
|
type: number
|
||||||
|
format: decimal
|
||||||
|
default: 0.0
|
||||||
|
minimum: 0.0
|
||||||
UserSerializerrr:
|
UserSerializerrr:
|
||||||
required:
|
required:
|
||||||
- username
|
- username
|
||||||
@@ -1121,8 +1103,3 @@ definitions:
|
|||||||
pattern: ^[-a-zA-Z0-9_]+$
|
pattern: ^[-a-zA-Z0-9_]+$
|
||||||
readOnly: true
|
readOnly: true
|
||||||
uniqueItems: true
|
uniqueItems: true
|
||||||
securityDefinitions:
|
|
||||||
basic:
|
|
||||||
type: basic
|
|
||||||
security:
|
|
||||||
- basic: []
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
envlist =
|
envlist =
|
||||||
py27-django111-drf37,
|
py27-django111-drf37,
|
||||||
py{34,35,36}-django{111,20}-drf37,
|
py{34,35,36}-django{111,20}-drf37,
|
||||||
py36-drfmaster,
|
py36-django20-drfmaster,
|
||||||
lint, docs
|
lint, docs
|
||||||
|
|
||||||
[travis:env]
|
[travis:env]
|
||||||
@@ -17,9 +17,8 @@ deps =
|
|||||||
|
|
||||||
drf37: djangorestframework>=3.7.7,<3.8
|
drf37: djangorestframework>=3.7.7,<3.8
|
||||||
|
|
||||||
# test with the latest build of Django and django-rest-framework to get early warning of compatibility issues
|
# test with the latest build of django-rest-framework to get early warning of compatibility issues
|
||||||
drfmaster: https://github.com/encode/django-rest-framework/archive/master.tar.gz
|
drfmaster: https://github.com/encode/django-rest-framework/archive/master.tar.gz
|
||||||
drfmaster: https://github.com/django/django/archive/master.tar.gz
|
|
||||||
|
|
||||||
# other dependencies
|
# other dependencies
|
||||||
-rrequirements/validation.txt
|
-rrequirements/validation.txt
|
||||||
@@ -28,9 +27,6 @@ deps =
|
|||||||
commands =
|
commands =
|
||||||
pytest --cov --cov-config .coveragerc --cov-append --cov-report="" {posargs}
|
pytest --cov --cov-config .coveragerc --cov-append --cov-report="" {posargs}
|
||||||
|
|
||||||
[testenv:py36-drfmaster]
|
|
||||||
pip_pre = True
|
|
||||||
|
|
||||||
[testenv:lint]
|
[testenv:lint]
|
||||||
skip_install = true
|
skip_install = true
|
||||||
deps =
|
deps =
|
||||||
|
|||||||
Reference in New Issue
Block a user