Compare commits

..

24 Commits

Author SHA1 Message Date
Cristi Vîjdea 6f7d14fdb2 Add 1.6.1 changelog 2018-04-01 12:08:14 +03:00
shahha 941ec8fdaf Added SUPPORTED_SUBMIT_METHODS swagger ui setting (#91) 2018-03-26 11:15:28 +03:00
Cristi Vîjdea 51ec07261d Improve OAuth2 behaviour and documentation (#90)
* Set OAUTH2_REDIRECT_URL to oauth2-redirect.html by default
* Add example SWAGGER_SETTINGS for OAuth
* Add note about redirect URL
2018-03-24 13:05:37 +02:00
Cristi Vîjdea a9cdf6d561 Fix type of properties in Schema docstring
Fixes #87.
2018-03-21 13:39:14 +02:00
Cristi Vîjdea a64ce95e3c Reset testproj migrations
Heroku deployment issues...
2018-03-18 20:35:02 +02:00
Cristi Vîjdea c9147a5746 Fix testproj people app 2018-03-18 20:20:04 +02:00
Cristi Vîjdea ccdf646c0e Run isort 2018-03-18 20:01:21 +02:00
Cristi Vîjdea 04252ebec3 Remove reodc-alpha artifacts 2018-03-18 19:52:16 +02:00
Cristi Vîjdea 848fb69009 Update swagger-ui to 3.13.0 2018-03-18 19:32:21 +02:00
ko-pp 309a6eb8cd Fix schema generation with OneToOneFields (#81)
* Fix: OneToOneRel, used by OneToOneField doesn't have help_text nor primary_key attributes, thus breaking OpenAPISchemaGenerator; use hasattr() as safe-guard.
* Fix: use getattr() with a default value instead of hasattr() + acessing the value
* Add: 'people' app that breaks drf_yasg without previous commits
* Update tests/references.yaml + run isort and flake8
* Fix: set on_delete for Person.identity as Django-2+ requires it
2018-03-18 19:30:36 +02:00
Cristi Vîjdea a7fbba4967 Update changelog for 1.5.0 2018-03-12 19:26:12 +02:00
Cristi Vîjdea 66785b1ee8 Update swagger-ui to 3.12.1 2018-03-12 19:06:20 +02:00
John Carter fb240f6a5b Detect type of SlugRelatedField with readonly=True (#83)
* Added test for #82 - readonly SlugRelatedField not showing format uuid
* Added ArticleGroup model
* Added example of SlugRelatedField to a uuid field with and without readonly
* Check remote_field.model in get_related_model

Closes #82.
2018-03-10 13:29:20 +02:00
John Carter 2c459024d1 Handle django rest framework HiddenField (#79)
* Added HiddenFieldInspector (#78)
* Updated docs + added test for HiddenField

Added HiddenField to testproj (no change in yaml as expected)

Closes #78.
2018-03-09 22:46:47 +02:00
ko-pp b62161f762 Escape '.' wildcard in testproj urlregex (#80)
Fix: Django's URL patterns being regexes, '.' has to be escaped to be used as a literal value and not as a joker.
2018-03-09 18:28:12 +02:00
Cristi Vîjdea 9ad55bac99 Guard against attempted deletion of inexistent attributes
Fixes #76
2018-03-05 19:59:03 +02:00
Cristi Vîjdea 3d3b7899e5 Update swagger-ui to 3.12.0 and ReDoc to 1.21.2 2018-03-05 17:07:45 +02:00
Cristi Vîjdea 6ea8711a1f Fix in-place modification of swagger_auto_schema arguments (#75)
Fixes #74
2018-03-05 11:51:51 +02:00
Cristi Vîjdea ee46f59fb1 Infer ChoiceField type from model field when in ModelSerializer
Fixes part of issue #69
2018-02-26 20:32:57 +02:00
Santiago Castro 3f7ad62950 Make 1.10.0 the min six version (#71) 2018-02-26 17:18:52 +02:00
Cristi Vîjdea b38d3e6805 Do not set pattern on non-string values
Fixes #68
2018-02-23 18:51:55 +02:00
Cristi Vîjdea 058fd7096d Change README link from pypi.python.org to pypi.org
Description rendering is prettier on the new version.
2018-02-22 20:46:26 +02:00
Cristi Vîjdea a32321d43a Add release dates to changelog 2018-02-22 20:46:22 +02:00
Cristi Vîjdea f15e70b7db Fix missing assignment for default in openapi.Parameter 2018-02-22 20:33:47 +02:00
46 changed files with 668 additions and 184 deletions
+4 -3
View File
@@ -8,14 +8,15 @@
<option name="manageScript" value="manage.py" />
<option name="environment" value="&lt;map/&gt;" />
<option name="doNotUseTestRunner" value="false" />
<option name="trackFilePattern" value="migrations" />
</configuration>
</facet>
</component>
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/testproj" isTestSource="false" />
<excludeFolder url="file://$MODULE_DIR$/venv" />
</content>
<orderEntry type="jdk" jdkName="Python 3.6 (drf-yasg)" jdkType="Python SDK" />
<orderEntry type="sourceFolder" forTests="false" />
@@ -24,7 +25,7 @@
<option name="TEMPLATE_CONFIGURATION" value="Django" />
<option name="TEMPLATE_FOLDERS">
<list>
<option value="$MODULE_DIR$/drf_yasg/templates" />
<option value="$MODULE_DIR$/src/drf_yasg/templates" />
</list>
</option>
</component>
@@ -32,4 +33,4 @@
<option name="projectConfiguration" value="py.test" />
<option name="PROJECT_TEST_RUNNER" value="py.test" />
</component>
</module>
</module>
+2 -2
View File
@@ -134,7 +134,7 @@ In ``urls.py``:
)
urlpatterns = [
url(r'^swagger(?P<format>.json|.yaml)$', schema_view.without_ui(cache_timeout=None), name='schema-json'),
url(r'^swagger(?P<format>\.json|\.yaml)$', schema_view.without_ui(cache_timeout=None), name='schema-json'),
url(r'^swagger/$', schema_view.with_ui('swagger', cache_timeout=None), name='schema-swagger-ui'),
url(r'^redoc/$', schema_view.with_ui('redoc', cache_timeout=None), name='schema-redoc'),
...
@@ -362,7 +362,7 @@ provided out of the box - if you have ``djangorestframework-camel-case`` install
:alt: Codecov
.. |pypi-version| image:: https://img.shields.io/pypi/v/drf-yasg.svg
:target: https://pypi.python.org/pypi/drf-yasg/
:target: https://pypi.org/project/drf-yasg/
:alt: PyPI
.. |rtd-badge| image:: https://img.shields.io/readthedocs/drf-yasg.svg
+121
View File
@@ -2,10 +2,99 @@
Changelog
#########
*********
**1.6.1**
*********
*Release date: Apr 01, 2018*
- **ADDED:** added ``SUPPORTED_SUBMIT_METHODS`` ``swagger-ui`` setting
*********
**1.6.0**
*********
*Release date: Mar 24, 2018*
- **IMPROVED:** ``OAUTH2_REDIRECT_URL`` will now default to the built in ``oauth2-redirect.html`` file
*********
**1.5.1**
*********
*Release date: Mar 18, 2018*
- **IMPROVED:** updated ``swagger-ui`` to version 3.13.0
- **FIXED:** fixed a crash caused by ``serializers.OneToOneRel`` (:pr:`81`, thanks to :ghuser:`ko-pp`)
*********
**1.5.0**
*********
*Release date: Mar 12, 2018*
- **IMPROVED:** ``serializers.HiddenField`` are now hidden (:issue:`78`, :pr:`79`, thanks to :ghuser:`therefromhere`)
*NOTE:* in order for this to work, you will have to add the new ``drf_yasg.inspectors.HiddenFieldInspector`` to your
``DEFAULT_FIELD_INSPECTORS`` array if you changed it from the default value
- **IMPROVED:** type of model field is now detected for ``serializers.SlugRelatedField`` with ``read_only=True``
(:issue:`82`, :pr:`83`, thanks to :ghuser:`therefromhere`)
*********
**1.4.7**
*********
*Release date: Mar 05, 2018*
- **FIXED:** prevent crashes caused by attempting to delete object attributes which do not exist in the first place
(:issue:`76`)
*********
**1.4.6**
*********
*Release date: Mar 05, 2018*
- **IMPROVED:** updated ``swagger-ui`` to version 3.12.0
- **IMPROVED:** updated ``ReDoc`` to version 1.21.2
*********
**1.4.5**
*********
*Release date: Mar 05, 2018*
- **FIXED:** fixed an issue with modification of ``swagger_auto_schema`` arguments in-place during introspection, which
would sometimes cause an incomplete Swagger document to be generated after the first pass (:issue:`74`, :pr:`75`)
*********
**1.4.4**
*********
*Release date: Feb 26, 2018*
- **IMPROVED:** ``type`` for ``ChoiceField`` generated by a ``ModelSerializer`` from a model field with ``choices=...``
will now be set according to the associated model field (:issue:`69`)
- **FIXED:** ``lookup_field`` and ``lookup_value_regex`` on the same ``ViewSet`` will no longer trigger an exception
(:issue:`68`)
*********
**1.4.3**
*********
*Release date: Feb 22, 2018*
- **FIXED:** added a missing assignment that would cause the ``default`` argument to ``openapi.Parameter.__init__`` to
be ignored
*********
**1.4.2**
*********
*Release date: Feb 22, 2018*
- **FIXED:** fixed a bug that causes a ``ModelViewSet`` generated from models with nested ``ForeignKey`` to output
models named ``Nested`` into the ``definitions`` section (:issue:`59`, :pr:`65`)
- **FIXED:** ``Response`` objects without a ``schema`` are now properly handled when passed through
@@ -15,6 +104,8 @@ Changelog
**1.4.1**
*********
*Release date: Feb 21, 2018*
- **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
@@ -26,6 +117,8 @@ Changelog
**1.4.0**
*********
*Release date: Feb 04, 2018*
- **ADDED:** added settings for OAuth2 client configuration in ``swagger-ui`` (:issue:`53`)
- **IMPROVED:** updated ``swagger-ui`` to version 3.9.3
@@ -33,6 +126,8 @@ Changelog
**1.3.1**
*********
*Release date: Jan 24, 2018*
- **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
@@ -42,6 +137,8 @@ Changelog
**1.3.0**
*********
*Release date: Jan 23, 2018*
- **ADDED:** security requirements are now correctly set and can be customized; this should fix problems related
to authentication in ``swagger-ui`` Try it out! (:issue:`50`, :pr:`54`)
- **IMPROVED:** updated ``swagger-ui`` to version 3.9.2
@@ -52,6 +149,8 @@ Changelog
**1.2.2**
*********
*Release date: Jan 12, 2018*
- **FIXED:** djangorestframework>=3.7.7 is now required because of breaking changes
(:issue:`44`, :pr:`45`, thanks to :ghuser:`h-hirokawa`)
@@ -59,12 +158,16 @@ Changelog
**1.2.1**
*********
*Release date: Jan 12, 2018*
- Fixed deployment issues
*********
**1.2.0**
*********
*Release date: Jan 12, 2018 (missing from PyPI due to deployment issues)*
- **ADDED:** ``basePath`` is now generated by taking into account the ``SCRIPT_NAME`` variable and the
longest common prefix of API urls (:issue:`37`, :pr:`42`)
- **IMPROVED:** removed inline scripts and styles from bundled HTML templates to increase CSP compatibility
@@ -76,6 +179,8 @@ Changelog
**1.1.3**
*********
*Release date: Jan 02, 2018*
- **FIXED:** schema view cache will now always ``Vary`` on the ``Cookie`` and ``Authentication`` (the
``Vary`` header was previously only added if ``public`` was set to ``True``) - this fixes issues related to Django
authentication in ``swagger-ui`` and ``CurrentUserDefault`` values in the schema
@@ -84,6 +189,8 @@ Changelog
**1.1.2**
*********
*Release date: Jan 01, 2018*
- **IMPROVED:** updated ``swagger-ui`` to version 3.8.1
- **IMPROVED:** removed some unneeded static files
@@ -91,6 +198,8 @@ Changelog
**1.1.1**
*********
*Release date: Dec 27, 2017*
- **ADDED:** :ref:`generate_swagger management command <management-command>`
(:issue:`29`, :pr:`31`, thanks to :ghuser:`beaugunderson`)
- **FIXED:** fixed improper generation of ``\Z`` regex tokens - will now be repalced by ``$``
@@ -99,6 +208,8 @@ Changelog
**1.1.0**
*********
*Release date: Dec 27, 2017*
- **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>`
@@ -114,6 +225,8 @@ Changelog
**1.0.6**
*********
*Release date: Dec 23, 2017*
- **FIXED:** Swagger UI "Try it out!" should now work with Django login
- **FIXED:** callable ``default`` values on serializer fields will now be properly called (:pr:`24`, :issue:`25`)
- **IMPROVED:** updated ``swagger-ui`` to version 3.8.0
@@ -125,6 +238,8 @@ Changelog
**1.0.5**
*********
*Release date: Dec 18, 2017*
- **FIXED:** fixed a crash caused by having read-only Serializers nested by reference
- **FIXED:** removed erroneous backslashes in paths when routes are generated using Django 2
`path() <https://docs.djangoproject.com/en/2.0/ref/urls/#django.urls.path>`_
@@ -136,6 +251,8 @@ Changelog
**1.0.4**
*********
*Release date: Dec 16, 2017*
- **FIXED:** fixed improper generation of YAML references
- **ADDED:** added ``query_serializer`` parameter to
:func:`@swagger_auto_schema <.swagger_auto_schema>` (:issue:`16`, :pr:`17`)
@@ -144,6 +261,8 @@ Changelog
**1.0.3**
*********
*Release date: Dec 15, 2017*
- **FIXED:** fixed bug that caused schema views returned from cache to fail (:issue:`14`)
- **FIXED:** disabled automatic generation of response schemas for form operations to avoid confusing errors caused by
attempting to shove file parameters into Schema objects
@@ -152,4 +271,6 @@ Changelog
**1.0.2**
*********
*Release date: Dec 13, 2017*
- First published version
+1 -1
View File
@@ -273,7 +273,7 @@ def role_github_user(name, rawtext, text, lineno, inliner, options=None, content
options = options or {}
content = content or []
if not re.match(r"^[a-z\d](?:[a-z\d]|-(?=[a-z\d])){0,38}$", text):
if not re.match(r"^[a-z\d](?:[a-z\d]|-(?=[a-z\d])){0,38}$", text, re.IGNORECASE):
return sphinx_err(inliner, lineno, rawtext, '"%s" is not a valid GitHub username.' % text)
ref = gh_user_uri.format(text)
+34
View File
@@ -63,3 +63,37 @@ settings described below were added as a result of discussion in issue :issue:`5
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.
Example
^^^^^^^
A very simple working configuration was provided by :ghuser:`Vigrond`, originally at
`https://github.com/Vigrond/django_oauth2_example <https://github.com/Vigrond/django_oauth2_example>`_.
.. code-block:: python
SWAGGER_SETTINGS = {
'USE_SESSION_AUTH': False,
'SECURITY_DEFINITIONS': {
'Your App API - Swagger': {
'type': 'oauth2',
'authorizationUrl': '/yourapp/o/authorize',
'tokenUrl': '/yourapp/o/token/',
'flow": "accessCode',
'scopes': {
'read:groups': 'read groups',
}
}
},
'OAUTH2_CONFIG': {
'clientId': 'yourAppClientId',
'clientSecret': 'yourAppClientSecret',
'appName': 'your application name'
},
}
If the OAuth2 provider requires you to provide the full absolute redirect URL, the default value for most
``staticfiles`` configurations will be ``<origin>/static/drf-yasg/swagger-ui-dist/oauth2-redirect.html``. If this is
not suitable for some reason, you can override the ``OAUTH2_REDIRECT_URL`` setting as appropriate.
+14 -1
View File
@@ -66,6 +66,7 @@ to this list.
: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.HiddenFieldInspector' <.inspectors.HiddenFieldInspector>`, |br| \
:class:`'drf_yasg.inspectors.SimpleFieldInspector' <.inspectors.SimpleFieldInspector>`, |br| \
:class:`'drf_yasg.inspectors.StringDefaultFieldInspector' <.inspectors.StringDefaultFieldInspector>`, |br| \
``]``
@@ -258,7 +259,10 @@ Controls how many levels are expaned by default when showing nested models.
OAUTH2_REDIRECT_URL
-------------------
Used when OAuth2 authenitcation of API requests via swagger-ui is desired.
Used when OAuth2 authenitcation of API requests via swagger-ui is desired. If ``None`` is passed, the
``oauth2RedirectUrl`` parameter will be set to ``{% static 'drf-yasg/swagger-ui-dist/oauth2-redirect.html' %}``. This
is the default `https://github.com/swagger-api/swagger-ui/blob/master/dist/oauth2-redirect.html <oauth2-redirect>`_
file provided by ``swagger-ui``.
**Default**: :python:`None` |br|
*Maps to parameter*: ``oauth2RedirectUrl``
@@ -272,6 +276,15 @@ to the ``SwaggerUIBundle#initOAuth`` method, and must be a dictionary. See
**Default**: :python:`{}`
SUPPORTED_SUBMIT_METHODS
------------------------
List of HTTP methods that have the Try it out feature enabled. An empty array disables Try it out for all operations.
This does not filter the operations from the display.
**Default**: :python:`['get','put','post','delete','options','head','patch','trace']` |br|
*Maps to parameter*: ``supportedSubmitMethods``
******************
``REDOC_SETTINGS``
******************
+10 -10
View File
@@ -211,9 +211,9 @@
"integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw=="
},
"readable-stream": {
"version": "2.3.4",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.4.tgz",
"integrity": "sha512-vuYxeWYM+fde14+rajzqgeohAI7YoJcHE7kXDAc4Nk0EbuKnJfqtY9YtRkLo/tqkuF7MsBQRhPnPeyjYITp3ZQ==",
"version": "2.3.5",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.5.tgz",
"integrity": "sha512-tK0yDhrkygt/knjowCUiWP9YdV7c5R+8cR0r/kt9ZhBU906Fs6RpQJCEilamRJj1Nx2rWI6LkW9gKqjTkshhEw==",
"requires": {
"core-util-is": "1.0.2",
"inherits": "2.0.3",
@@ -225,9 +225,9 @@
}
},
"redoc": {
"version": "1.21.0",
"resolved": "https://registry.npmjs.org/redoc/-/redoc-1.21.0.tgz",
"integrity": "sha1-RY8E7b7MqyVbQORhZA0eRG949N0=",
"version": "1.21.2",
"resolved": "https://registry.npmjs.org/redoc/-/redoc-1.21.2.tgz",
"integrity": "sha1-uMeLfDxtwFjmeZniwvYbqFjewaY=",
"requires": {
"core-js": "2.5.3",
"dropkickjs": "2.1.10",
@@ -301,7 +301,7 @@
"requires": {
"builtin-status-codes": "3.0.0",
"inherits": "2.0.3",
"readable-stream": "2.3.4",
"readable-stream": "2.3.5",
"to-arraybuffer": "1.0.1",
"xtend": "4.0.1"
}
@@ -315,9 +315,9 @@
}
},
"swagger-ui-dist": {
"version": "3.10.0",
"resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-3.10.0.tgz",
"integrity": "sha1-ilrzP/ImPHFaFD9z8qjUfVOZQ+8="
"version": "3.13.0",
"resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-3.13.0.tgz",
"integrity": "sha1-NPpclKDx3EAWVBmIEV38rhOKirE="
},
"tiny-emitter": {
"version": "2.0.2",
+2 -2
View File
@@ -1,8 +1,8 @@
{
"name": "drf-yasg",
"dependencies": {
"redoc": "^1.21.0",
"swagger-ui-dist": "^3.10.0"
"redoc": "^1.21.2",
"swagger-ui-dist": "^3.13.0"
},
"repository": {
"type": "git",
+1 -1
View File
@@ -4,7 +4,7 @@ openapi_codec>=1.3.2
ruamel.yaml>=0.15.34
inflection>=0.3.1
future>=0.16.0
six>=1.11.0
six>=1.10.0
uritemplate>=3.0.0
djangorestframework>=3.7.7
+11
View File
@@ -11,6 +11,7 @@ SWAGGER_DEFAULTS = {
'drf_yasg.inspectors.ChoiceFieldInspector',
'drf_yasg.inspectors.FileFieldInspector',
'drf_yasg.inspectors.DictFieldInspector',
'drf_yasg.inspectors.HiddenFieldInspector',
'drf_yasg.inspectors.SimpleFieldInspector',
'drf_yasg.inspectors.StringDefaultFieldInspector',
],
@@ -45,6 +46,16 @@ SWAGGER_DEFAULTS = {
'DEFAULT_MODEL_DEPTH': 3,
'OAUTH2_REDIRECT_URL': None,
'OAUTH2_CONFIG': {},
'SUPPORTED_SUBMIT_METHODS': [
'get',
'put',
'post',
'delete',
'options',
'head',
'patch',
'trace'
],
}
REDOC_DEFAULTS = {
+8 -7
View File
@@ -1,3 +1,4 @@
import copy
import logging
import re
from collections import OrderedDict, defaultdict
@@ -355,9 +356,9 @@ class OpenAPISchemaGenerator(object):
view_inspector = view_inspector_cls(view, path, method, components, request, overrides)
operation = view_inspector.get_operation(operation_keys)
if set(operation.consumes) == set(self.consumes):
if 'consumes' in operation and set(operation.consumes) == set(self.consumes):
del operation.consumes
if set(operation.produces) == set(self.produces):
if 'produces' in operation and set(operation.produces) == set(self.produces):
del operation.produces
return operation
@@ -388,7 +389,7 @@ class OpenAPISchemaGenerator(object):
if method in overrides:
overrides = overrides[method]
return overrides
return copy.deepcopy(overrides)
def get_path_parameters(self, path, view_cls):
"""Return a list of Parameter instances corresponding to any templated path variables.
@@ -405,12 +406,12 @@ class OpenAPISchemaGenerator(object):
for variable in uritemplate.variables(path):
model, model_field = get_queryset_field(queryset, variable)
attrs = get_basic_type_info(model_field) or {'type': openapi.TYPE_STRING}
if hasattr(view_cls, 'lookup_value_regex') and getattr(view_cls, 'lookup_field', None) == variable:
attrs['pattern'] = view_cls.lookup_value_regex
if getattr(view_cls, 'lookup_field', None) == variable and attrs['type'] == openapi.TYPE_STRING:
attrs['pattern'] = getattr(view_cls, 'lookup_value_regex', attrs.get('pattern', None))
if model_field and model_field.help_text:
if model_field and getattr(model_field, 'help_text', False):
description = force_text(model_field.help_text)
elif model_field and model_field.primary_key:
elif model_field and getattr(model_field, 'primary_key', False):
description = get_pk_description(model, model_field)
else:
description = None
+4 -3
View File
@@ -3,8 +3,9 @@ from .base import (
BaseInspector, FieldInspector, FilterInspector, NotHandled, PaginatorInspector, SerializerInspector, ViewInspector
)
from .field import (
CamelCaseJSONFilter, ChoiceFieldInspector, DictFieldInspector, FileFieldInspector, InlineSerializerInspector,
ReferencingSerializerInspector, RelatedFieldInspector, SimpleFieldInspector, StringDefaultFieldInspector
CamelCaseJSONFilter, ChoiceFieldInspector, DictFieldInspector, FileFieldInspector, HiddenFieldInspector,
InlineSerializerInspector, ReferencingSerializerInspector, RelatedFieldInspector, SimpleFieldInspector,
StringDefaultFieldInspector
)
from .query import CoreAPICompatInspector, DjangoRestResponsePagination
from .view import SwaggerAutoSchema
@@ -24,7 +25,7 @@ __all__ = [
# field inspectors
'InlineSerializerInspector', 'ReferencingSerializerInspector', 'RelatedFieldInspector', 'SimpleFieldInspector',
'FileFieldInspector', 'ChoiceFieldInspector', 'DictFieldInspector', 'StringDefaultFieldInspector',
'CamelCaseJSONFilter',
'CamelCaseJSONFilter', 'HiddenFieldInspector',
# view inspectors
'SwaggerAutoSchema',
+42 -14
View File
@@ -89,9 +89,9 @@ class InlineSerializerInspector(SerializerInspector):
properties=properties,
required=required or None,
)
if not ref_name:
if not ref_name and 'title' in result:
# on an inline model, the title is derived from the field name
# but is visually displayed like the model named, which is confusing
# but is visually displayed like the model name, which is confusing
# it is better to just remove title from inline models
del result.title
return result
@@ -161,7 +161,11 @@ def get_related_model(model, source):
:return: related model or ``None``
"""
try:
return getattr(model, source).rel.related_model
descriptor = getattr(model, source)
try:
return descriptor.rel.related_model
except Exception:
return descriptor.field.remote_field.model
except Exception: # pragma: no cover
return None
@@ -361,7 +365,10 @@ def get_basic_type_info(field):
else: # pragma: no cover
return None
pattern = find_regex(field) if format in (None, openapi.FORMAT_SLUG) else None
pattern = None
if swagger_type == openapi.TYPE_STRING and format in (None, openapi.FORMAT_SLUG):
pattern = find_regex(field)
limits = find_limits(field)
result = OrderedDict([
@@ -394,16 +401,27 @@ class ChoiceFieldInspector(FieldInspector):
def field_to_swagger_object(self, field, swagger_object_type, use_references, **kwargs):
SwaggerType, ChildSwaggerType = self._get_partial_types(field, swagger_object_type, use_references, **kwargs)
if isinstance(field, serializers.MultipleChoiceField):
return SwaggerType(
type=openapi.TYPE_ARRAY,
items=ChildSwaggerType(
type=openapi.TYPE_STRING,
enum=list(field.choices.keys())
if isinstance(field, serializers.ChoiceField):
enum_type = openapi.TYPE_STRING
# for ModelSerializer, try to infer the type from the associated model field
serializer = get_parent_serializer(field)
if isinstance(serializer, serializers.ModelSerializer):
model = getattr(getattr(serializer, 'Meta'), 'model')
model_field = get_model_field(model, field.source)
if model_field:
enum_type = get_basic_type_info(model_field).get('type', enum_type)
if isinstance(field, serializers.MultipleChoiceField):
return SwaggerType(
type=openapi.TYPE_ARRAY,
items=ChildSwaggerType(
type=enum_type,
enum=list(field.choices.keys())
)
)
)
elif isinstance(field, serializers.ChoiceField):
return SwaggerType(type=openapi.TYPE_STRING, enum=list(field.choices.keys()))
return SwaggerType(type=enum_type, enum=list(field.choices.keys()))
return NotHandled
@@ -451,11 +469,21 @@ class DictFieldInspector(FieldInspector):
return NotHandled
class HiddenFieldInspector(FieldInspector):
"""Hide ``HiddenField``."""
def field_to_swagger_object(self, field, swagger_object_type, use_references, **kwargs):
if isinstance(field, serializers.HiddenField):
return None
return NotHandled
class StringDefaultFieldInspector(FieldInspector):
"""For otherwise unhandled fields, return them as plain :data:`.TYPE_STRING` objects."""
def field_to_swagger_object(self, field, swagger_object_type, use_references, **kwargs): # pragma: no cover
# TODO unhandled fields: TimeField HiddenField JSONField
# TODO unhandled fields: TimeField JSONField
SwaggerType, ChildSwaggerType = self._get_partial_types(field, swagger_object_type, use_references, **kwargs)
return SwaggerType(type=openapi.TYPE_STRING)
+2 -1
View File
@@ -396,6 +396,7 @@ class Parameter(SwaggerDict):
self.enum = enum
self.pattern = pattern
self.items = items
self.default = default
self._insert_extras__()
if self['in'] == IN_PATH:
# path parameters must always be required
@@ -424,7 +425,7 @@ class Schema(SwaggerDict):
:param str format: value format, see OpenAPI spec
:param list enum: restrict possible values
:param str pattern: pattern if type is ``string``
:param list[.Schema,.SchemaRef] properties: object properties; required if `type` is ``object``
:param dict[str,(.Schema,.SchemaRef)] properties: object properties; required if `type` is ``object``
:param bool,.Schema,.SchemaRef additional_properties: allow wildcard properties not listed in `properties`
:param list[str] required: list of requried property names
:param .Schema,.SchemaRef items: type of array items, only valid if `type` is ``array``
+1 -8
View File
@@ -94,6 +94,7 @@ class _UIRenderer(BaseRenderer):
'defaultModelExpandDepth': swagger_settings.DEFAULT_MODEL_DEPTH,
'defaultModelsExpandDepth': swagger_settings.DEFAULT_MODEL_DEPTH,
'oauth2RedirectUrl': swagger_settings.OAUTH2_REDIRECT_URL,
'supportedSubmitMethods': swagger_settings.SUPPORTED_SUBMIT_METHODS,
}
data = {k: v for k, v in data.items() if v is not None}
if swagger_settings.VALIDATOR_URL != '':
@@ -131,11 +132,3 @@ class ReDocRenderer(_UIRenderer):
"""
template = 'drf-yasg/redoc.html'
format = 'redoc'
class ReDocAlphaRenderer(_UIRenderer):
"""Renders a ReDoc web interface for schema browisng.
Also requires :class:`.OpenAPIRenderer` as an available renderer on the same view.
"""
template = 'drf-yasg/redoc-alpha.html'
format = 'redoc'
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -45,11 +45,18 @@
oauth2.auth.code = qp.code;
oauth2.callback({auth: oauth2.auth, redirectUrl: redirectUrl});
} else {
let oauthErrorMsg
if (qp.error) {
oauthErrorMsg = "["+qp.error+"]: " +
(qp.error_description ? qp.error_description+ ". " : "no accessCode received from the server. ") +
(qp.error_uri ? "More info: "+qp.error_uri : "");
}
oauth2.errCb({
authId: oauth2.auth.name,
source: "auth",
level: "error",
message: "Authorization failed: no accessCode received from the server"
message: oauthErrorMsg || "[Authorization failed]: no accessCode received from the server"
});
}
} else {
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
@@ -38,7 +38,7 @@ function initSwaggerUi() {
],
layout: "StandaloneLayout",
filter: true,
requestInterceptor: function(request) {
requestInterceptor: function (request) {
var headers = request.headers || {};
var csrftoken = document.querySelector("[name=csrfmiddlewaretoken]");
if (csrftoken) {
@@ -49,8 +49,15 @@ function initSwaggerUi() {
};
var swaggerSettings = JSON.parse(document.getElementById('swagger-settings').innerHTML);
console.log(swaggerSettings);
if (!('oauth2RedirectUrl' in swaggerSettings)) {
var oauth2RedirectUrl = document.getElementById('oauth2-redirect-url');
if (oauth2RedirectUrl) {
swaggerSettings['oauth2RedirectUrl'] = oauth2RedirectUrl.href;
oauth2RedirectUrl.parentNode.removeChild(oauth2RedirectUrl);
}
}
console.log(swaggerSettings);
for (var p in swaggerSettings) {
if (swaggerSettings.hasOwnProperty(p)) {
swaggerConfig[p] = swaggerSettings[p];
@@ -67,6 +67,8 @@
<script src="{% static 'drf-yasg/insQ.min.js' %}"></script>
<script src="{% static 'drf-yasg/swagger-ui-init.js' %}"></script>
<a id="oauth2-redirect-url" href="{% static 'drf-yasg/swagger-ui-dist/oauth2-redirect.html' %}" class="hidden"></a>
<div id="django-session-auth" class="hidden">
{% if USE_SESSION_AUTH %}
{% csrf_token %}
+9 -4
View File
@@ -11,10 +11,15 @@ from rest_framework.views import APIView
logger = logging.getLogger(__name__)
#: used to forcibly remove the body of a request via :func:`.swagger_auto_schema`
no_body = object()
unset = object()
class no_body(object):
"""Used as a sentinel value to forcibly remove the body of a request via :func:`.swagger_auto_schema`."""
pass
class unset(object):
"""Used as a sentinel value for function parameters not set by the caller where ``None`` would be a valid value."""
pass
def swagger_auto_schema(method=None, methods=None, auto_schema=unset, request_body=None, query_serializer=None,
@@ -33,7 +38,7 @@ def swagger_auto_schema(method=None, methods=None, auto_schema=unset, request_bo
: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``
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 :class:`.no_body`. The value given here
will be used as the ``schema`` property of a :class:`.Parameter` with ``in: 'body'``.
A Schema or SchemaRef is not valid if this request consumes form-data, because ``form`` and ``body`` parameters
+1 -4
View File
@@ -12,15 +12,12 @@ from rest_framework.views import APIView
from .app_settings import swagger_settings
from .generators import OpenAPISchemaGenerator
from .renderers import (
OpenAPIRenderer, ReDocAlphaRenderer, ReDocRenderer, SwaggerJSONRenderer, SwaggerUIRenderer, SwaggerYAMLRenderer
)
from .renderers import OpenAPIRenderer, ReDocRenderer, SwaggerJSONRenderer, SwaggerUIRenderer, SwaggerYAMLRenderer
SPEC_RENDERERS = (SwaggerYAMLRenderer, SwaggerJSONRenderer, OpenAPIRenderer)
UI_RENDERERS = {
'swagger': (SwaggerUIRenderer, ReDocRenderer),
'redoc': (ReDocRenderer, SwaggerUIRenderer),
'redoc-alpha': (ReDocAlphaRenderer, ReDocRenderer, SwaggerUIRenderer)
}
+22 -1
View File
@@ -1,8 +1,9 @@
# Generated by Django 2.0 on 2017-12-23 09:07
# Generated by Django 2.0.1 on 2018-03-18 18:32
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import uuid
class Migration(migrations.Migration):
@@ -23,8 +24,28 @@ class Migration(migrations.Migration):
('slug', models.SlugField(blank=True, help_text='slug model help_text', unique=True)),
('date_created', models.DateTimeField(auto_now_add=True)),
('date_modified', models.DateTimeField(auto_now=True)),
('article_type', models.PositiveSmallIntegerField(choices=[(1, 'first'), (2, 'second'), (3, 'third'), (7, 'seven'), (8, 'eight')], help_text='IntegerField declared on model with choices=(...) and exposed via ModelSerializer', null=True)),
('cover', models.ImageField(blank=True, upload_to='article/original/')),
('author', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='articles', to=settings.AUTH_USER_MODEL)),
],
),
migrations.CreateModel(
name='ArticleGroup',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('uuid', models.UUIDField(default=uuid.uuid4, editable=False, unique=True)),
('title', models.CharField(help_text='title model help_text', max_length=255, unique=True)),
('slug', models.SlugField(blank=True, help_text='slug model help_text', unique=True)),
],
),
migrations.AddField(
model_name='article',
name='group',
field=models.ForeignKey(blank=True, default=None, on_delete=django.db.models.deletion.PROTECT, related_name='articles_as_main', to='articles.ArticleGroup'),
),
migrations.AddField(
model_name='article',
name='original_group',
field=models.ForeignKey(blank=True, default=None, on_delete=django.db.models.deletion.PROTECT, related_name='articles_as_original', to='articles.ArticleGroup'),
),
]
+17
View File
@@ -1,3 +1,5 @@
import uuid
from django.db import models
@@ -8,5 +10,20 @@ class Article(models.Model):
date_created = models.DateTimeField(auto_now_add=True)
date_modified = models.DateTimeField(auto_now=True)
author = models.ForeignKey('auth.User', related_name='articles', on_delete=models.CASCADE)
article_type = models.PositiveSmallIntegerField(
help_text="IntegerField declared on model with choices=(...) and exposed via ModelSerializer",
choices=((1, "first"), (2, "second"), (3, "third"), (7, "seven"), (8, "eight")), null=True
)
cover = models.ImageField(upload_to='article/original/', blank=True)
group = models.ForeignKey('ArticleGroup', related_name='articles_as_main', blank=True, default=None,
on_delete=models.PROTECT)
original_group = models.ForeignKey('ArticleGroup', related_name='articles_as_original', blank=True, default=None,
on_delete=models.PROTECT)
class ArticleGroup(models.Model):
uuid = models.UUIDField(default=uuid.uuid4, editable=False, unique=True)
title = models.CharField(help_text="title model help_text", max_length=255, blank=False, unique=True)
slug = models.SlugField(help_text="slug model help_text", unique=True, blank=True)
+5 -3
View File
@@ -1,7 +1,7 @@
from django.utils.translation import ugettext_lazy as _
from rest_framework import serializers
from articles.models import Article
from articles.models import Article, ArticleGroup
class ArticleSerializer(serializers.ModelSerializer):
@@ -12,11 +12,13 @@ class ArticleSerializer(serializers.ModelSerializer):
)
uuid = serializers.UUIDField(help_text="should articles have UUIDs?", read_only=True)
cover_name = serializers.FileField(use_url=False, source='cover', read_only=True)
group = serializers.SlugRelatedField(slug_field='uuid', queryset=ArticleGroup.objects.all())
original_group = serializers.SlugRelatedField(slug_field='uuid', read_only=True)
class Meta:
model = Article
fields = ('title', 'author', 'body', 'slug', 'date_created', 'date_modified',
'references', 'uuid', 'cover', 'cover_name')
'references', 'uuid', 'cover', 'cover_name', 'article_type', 'group', 'original_group', )
read_only_fields = ('date_created', 'date_modified',
'references', 'uuid', 'cover_name')
lookup_field = 'slug'
@@ -31,7 +33,7 @@ class ArticleSerializer(serializers.ModelSerializer):
class ImageUploadSerializer(serializers.Serializer):
what_am_i_doing = serializers.RegexField(regex=r"^69$", help_text="test")
what_am_i_doing = serializers.RegexField(regex=r"^69$", help_text="test", default="69")
image_styles = serializers.ListSerializer(
child=serializers.ChoiceField(choices=['wide', 'tall', 'thumb', 'social']),
help_text="Parameter with Items"
View File
+5
View File
@@ -0,0 +1,5 @@
from django.apps import AppConfig
class PeopleConfig(AppConfig):
name = 'people'
@@ -0,0 +1,30 @@
# Generated by Django 2.0.1 on 2018-03-18 18:32
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='Identity',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('firstName', models.CharField(max_length=30, null=True)),
('lastName', models.CharField(max_length=30, null=True)),
],
),
migrations.CreateModel(
name='Person',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('identity', models.OneToOneField(on_delete=django.db.models.deletion.PROTECT, related_name='person', to='people.Identity')),
],
),
]
+10
View File
@@ -0,0 +1,10 @@
from django.db import models
class Identity(models.Model):
firstName = models.CharField(max_length=30, null=True)
lastName = models.CharField(max_length=30, null=True)
class Person(models.Model):
identity = models.OneToOneField(Identity, related_name='person', on_delete=models.PROTECT)
+23
View File
@@ -0,0 +1,23 @@
from rest_framework import serializers
from .models import Identity, Person
class IdentitySerializer(serializers.ModelSerializer):
class Meta:
model = Identity
fields = '__all__'
class PersonSerializer(serializers.ModelSerializer):
identity = IdentitySerializer()
class Meta:
model = Person
fields = '__all__'
def create(self, validated_data):
identity = Identity(**validated_data['identity'])
identity.save()
validated_data['identity'] = identity
return super().create(validated_data)
+26
View File
@@ -0,0 +1,26 @@
from django.conf.urls import url
from .views import IdentityViewSet, PersonViewSet
person_list = PersonViewSet.as_view({
'get': 'list',
'post': 'create'
})
person_detail = PersonViewSet.as_view({
'get': 'retrieve',
'patch': 'partial_update',
'delete': 'destroy'
})
identity_detail = IdentityViewSet.as_view({
'get': 'retrieve',
'patch': 'partial_update',
})
urlpatterns = (
url(r'^$', person_list, name='people-list'),
url(r'^(?P<pk>[0-9]+)$', person_detail, name='person-detail'),
url(r'^(?P<person>[0-9]+)/identity$', identity_detail,
name='person-identity'),
)
+16
View File
@@ -0,0 +1,16 @@
from rest_framework import viewsets
from .models import Identity, Person
from .serializers import IdentitySerializer, PersonSerializer
class PersonViewSet(viewsets.ModelViewSet):
model = Person
queryset = Person.objects
serializer_class = PersonSerializer
class IdentityViewSet(viewsets.ModelViewSet):
model = Identity
queryset = Identity.objects
serializer_class = IdentitySerializer
+1 -1
View File
@@ -1,4 +1,4 @@
# Generated by Django 2.0 on 2017-12-23 09:07
# Generated by Django 2.0.1 on 2018-03-18 18:32
from django.conf import settings
from django.db import migrations, models
+1
View File
@@ -27,6 +27,7 @@ INSTALLED_APPS = [
'users',
'articles',
'todo',
'people'
]
MIDDLEWARE = [
+2 -1
View File
@@ -51,7 +51,7 @@ urlpatterns = [
url(r'^swagger(?P<format>.json|.yaml)$', SchemaView.without_ui(cache_timeout=0), name='schema-json'),
url(r'^swagger/$', SchemaView.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui'),
url(r'^redoc/$', SchemaView.with_ui('redoc', cache_timeout=0), name='schema-redoc'),
url(r'^redoc-alpha/$', SchemaView.with_ui('redoc-alpha', cache_timeout=0), name='schema-redoc-alpha'),
url(r'^cached/swagger(?P<format>.json|.yaml)$', SchemaView.without_ui(cache_timeout=None), name='cschema-json'),
url(r'^cached/swagger/$', SchemaView.with_ui('swagger', cache_timeout=None), name='cschema-swagger-ui'),
url(r'^cached/redoc/$', SchemaView.with_ui('redoc', cache_timeout=None), name='cschema-redoc'),
@@ -63,5 +63,6 @@ urlpatterns = [
url(r'^articles/', include('articles.urls')),
url(r'^users/', include('users.urls')),
url(r'^todo/', include('todo.urls')),
url(r'^people/', include('people.urls')),
url(r'^plain/', plain_view),
]
+1 -3
View File
@@ -1,6 +1,4 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2018-02-21 23:26
from __future__ import unicode_literals
# Generated by Django 2.0.1 on 2018-03-18 18:32
from django.db import migrations, models
import django.db.models.deletion
+4 -1
View File
@@ -1,3 +1,4 @@
from django.utils import timezone
from rest_framework import serializers
from .models import Todo, TodoAnother, TodoYetAnother
@@ -6,7 +7,9 @@ from .models import Todo, TodoAnother, TodoYetAnother
class TodoSerializer(serializers.ModelSerializer):
class Meta:
model = Todo
fields = ('title',)
fields = ('title', 'a_hidden_field',)
a_hidden_field = serializers.HiddenField(default=timezone.now)
class TodoAnotherSerializer(serializers.ModelSerializer):
+3
View File
@@ -8,6 +8,9 @@ class TodoViewSet(viewsets.ReadOnlyModelViewSet):
queryset = Todo.objects.all()
serializer_class = TodoSerializer
lookup_field = 'id'
lookup_value_regex = '[0-9]+'
class TodoAnotherViewSet(viewsets.ReadOnlyModelViewSet):
queryset = TodoAnother.objects.all()
+154 -1
View File
@@ -198,9 +198,10 @@ paths:
- name: what_am_i_doing
in: formData
description: test
required: true
required: false
type: string
pattern: ^69$
default: '69'
- name: image_styles
in: formData
description: Parameter with Items
@@ -233,6 +234,113 @@ paths:
type: string
format: slug
pattern: '[a-z0-9]+(?:-[a-z0-9]+)'
/people/:
get:
operationId: people_list
description: ''
parameters: []
responses:
'200':
description: ''
schema:
type: array
items:
$ref: '#/definitions/Person'
tags:
- people
post:
operationId: people_create
description: ''
parameters:
- name: data
in: body
required: true
schema:
$ref: '#/definitions/Person'
responses:
'201':
description: ''
schema:
$ref: '#/definitions/Person'
tags:
- people
parameters: []
/people/{id}:
get:
operationId: people_read
description: ''
parameters: []
responses:
'200':
description: ''
schema:
$ref: '#/definitions/Person'
tags:
- people
patch:
operationId: people_partial_update
description: ''
parameters:
- name: data
in: body
required: true
schema:
$ref: '#/definitions/Person'
responses:
'200':
description: ''
schema:
$ref: '#/definitions/Person'
tags:
- people
delete:
operationId: people_delete
description: ''
parameters: []
responses:
'204':
description: ''
tags:
- people
parameters:
- name: id
in: path
description: A unique integer value identifying this person.
required: true
type: integer
/people/{person}/identity:
get:
operationId: people_identity_read
description: ''
parameters: []
responses:
'200':
description: ''
schema:
$ref: '#/definitions/Identity'
tags:
- people
patch:
operationId: people_identity_partial_update
description: ''
parameters:
- name: data
in: body
required: true
schema:
$ref: '#/definitions/Identity'
responses:
'200':
description: ''
schema:
$ref: '#/definitions/Identity'
tags:
- people
parameters:
- name: person
in: path
required: true
type: string
/plain/:
get:
operationId: plain_list
@@ -541,6 +649,7 @@ definitions:
required:
- title
- body
- group
type: object
properties:
title:
@@ -590,6 +699,50 @@ definitions:
cover_name:
type: string
readOnly: true
article_type:
description: IntegerField declared on model with choices=(...) and exposed
via ModelSerializer
type: integer
enum:
- 1
- 2
- 3
- 7
- 8
group:
type: string
format: uuid
original_group:
type: string
format: uuid
readOnly: true
Identity:
title: Identity
type: object
properties:
id:
title: ID
type: integer
readOnly: true
firstName:
title: FirstName
type: string
maxLength: 30
lastName:
title: LastName
type: string
maxLength: 30
Person:
required:
- identity
type: object
properties:
id:
title: ID
type: integer
readOnly: true
identity:
$ref: '#/definitions/Identity'
Project:
required:
- projectName
+34
View File
@@ -2,11 +2,14 @@ import json
from collections import OrderedDict
import pytest
from rest_framework import routers, serializers, viewsets
from rest_framework.response import Response
from drf_yasg import codecs, openapi
from drf_yasg.codecs import yaml_sane_load
from drf_yasg.errors import SwaggerGenerationError
from drf_yasg.generators import OpenAPISchemaGenerator
from drf_yasg.utils import swagger_auto_schema
def test_schema_is_valid(swagger, codec_yaml):
@@ -79,3 +82,34 @@ def test_securiy_requirements(swagger_settings, mock_schema_request):
swagger = generator.get_schema(mock_schema_request, public=True)
assert swagger['security'] == []
def test_replaced_serializer():
class DetailSerializer(serializers.Serializer):
detail = serializers.CharField()
class DetailViewSet(viewsets.ViewSet):
serializer_class = DetailSerializer
@swagger_auto_schema(responses={404: openapi.Response("Not found or Not accessible", DetailSerializer)})
def retrieve(self, request, pk=None):
serializer = DetailSerializer({'detail': None})
return Response(serializer.data)
router = routers.DefaultRouter()
router.register(r'details', DetailViewSet, base_name='details')
generator = OpenAPISchemaGenerator(
info=openapi.Info(title="Test generator", default_version="v1"),
version="v2",
url='',
patterns=router.urls
)
for _ in range(3):
swagger = generator.get_schema(None, True)
assert 'Detail' in swagger['definitions']
assert 'detail' in swagger['definitions']['Detail']['properties']
responses = swagger['paths']['/details/{id}/']['get']['responses']
assert '404' in responses
assert responses['404']['schema']['$ref'] == "#/definitions/Detail"
+1 -1
View File
@@ -66,4 +66,4 @@ known_third_party =
coreapi,coreschema,datadiff,dj_database_url,django,django_filters,djangorestframework_camel_case,flex,gunicorn,
inflection,pygments,pytest,rest_framework,ruamel,setuptools_scm,swagger_spec_validator,uritemplate,user_agents,
whitenoise
known_first_party = drf_yasg,testproj,articles,snippets,users,urlconfs
known_first_party = drf_yasg,testproj,articles,people,snippets,todo,users,urlconfs