Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6ea8711a1f | |||
| ee46f59fb1 | |||
| 3f7ad62950 | |||
| b38d3e6805 | |||
| 058fd7096d | |||
| a32321d43a | |||
| f15e70b7db | |||
| 64c280e222 | |||
| d5073081d9 |
Generated
+3
-3
@@ -13,9 +13,9 @@
|
|||||||
</component>
|
</component>
|
||||||
<component name="NewModuleRootManager">
|
<component name="NewModuleRootManager">
|
||||||
<content url="file://$MODULE_DIR$">
|
<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$/src" isTestSource="false" />
|
||||||
<sourceFolder url="file://$MODULE_DIR$/testproj" isTestSource="false" />
|
<sourceFolder url="file://$MODULE_DIR$/testproj" isTestSource="false" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/venv" />
|
||||||
</content>
|
</content>
|
||||||
<orderEntry type="jdk" jdkName="Python 3.6 (drf-yasg)" jdkType="Python SDK" />
|
<orderEntry type="jdk" jdkName="Python 3.6 (drf-yasg)" jdkType="Python SDK" />
|
||||||
<orderEntry type="sourceFolder" forTests="false" />
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
@@ -24,7 +24,7 @@
|
|||||||
<option name="TEMPLATE_CONFIGURATION" value="Django" />
|
<option name="TEMPLATE_CONFIGURATION" value="Django" />
|
||||||
<option name="TEMPLATE_FOLDERS">
|
<option name="TEMPLATE_FOLDERS">
|
||||||
<list>
|
<list>
|
||||||
<option value="$MODULE_DIR$/drf_yasg/templates" />
|
<option value="$MODULE_DIR$/src/drf_yasg/templates" />
|
||||||
</list>
|
</list>
|
||||||
</option>
|
</option>
|
||||||
</component>
|
</component>
|
||||||
@@ -32,4 +32,4 @@
|
|||||||
<option name="projectConfiguration" value="py.test" />
|
<option name="projectConfiguration" value="py.test" />
|
||||||
<option name="PROJECT_TEST_RUNNER" value="py.test" />
|
<option name="PROJECT_TEST_RUNNER" value="py.test" />
|
||||||
</component>
|
</component>
|
||||||
</module>
|
</module>
|
||||||
+1
-1
@@ -362,7 +362,7 @@ provided out of the box - if you have ``djangorestframework-camel-case`` install
|
|||||||
:alt: Codecov
|
:alt: Codecov
|
||||||
|
|
||||||
.. |pypi-version| image:: https://img.shields.io/pypi/v/drf-yasg.svg
|
.. |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
|
:alt: PyPI
|
||||||
|
|
||||||
.. |rtd-badge| image:: https://img.shields.io/readthedocs/drf-yasg.svg
|
.. |rtd-badge| image:: https://img.shields.io/readthedocs/drf-yasg.svg
|
||||||
|
|||||||
@@ -2,10 +2,53 @@
|
|||||||
Changelog
|
Changelog
|
||||||
#########
|
#########
|
||||||
|
|
||||||
|
|
||||||
|
*********
|
||||||
|
**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
|
||||||
|
``swagger_auto_schema`` (:issue:`66`)
|
||||||
|
|
||||||
*********
|
*********
|
||||||
**1.4.1**
|
**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
|
- **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`)
|
``DecimalField`` in the OpenAPI schema (:issue:`62`)
|
||||||
- **FIXED:** error responses from web UI views are now rendered with ``TemplateHTMLRenderer`` instead of throwing
|
- **FIXED:** error responses from web UI views are now rendered with ``TemplateHTMLRenderer`` instead of throwing
|
||||||
@@ -17,6 +60,8 @@ Changelog
|
|||||||
**1.4.0**
|
**1.4.0**
|
||||||
*********
|
*********
|
||||||
|
|
||||||
|
*Release date: Feb 04, 2018*
|
||||||
|
|
||||||
- **ADDED:** added settings for OAuth2 client configuration in ``swagger-ui`` (:issue:`53`)
|
- **ADDED:** added settings for OAuth2 client configuration in ``swagger-ui`` (:issue:`53`)
|
||||||
- **IMPROVED:** updated ``swagger-ui`` to version 3.9.3
|
- **IMPROVED:** updated ``swagger-ui`` to version 3.9.3
|
||||||
|
|
||||||
@@ -24,6 +69,8 @@ Changelog
|
|||||||
**1.3.1**
|
**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`)
|
- **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 ``produces`` based on renderer classes
|
||||||
- **IMPROVED:** added generation of top-level ``consumes`` and ``produces`` based on
|
- **IMPROVED:** added generation of top-level ``consumes`` and ``produces`` based on
|
||||||
@@ -33,6 +80,8 @@ Changelog
|
|||||||
**1.3.0**
|
**1.3.0**
|
||||||
*********
|
*********
|
||||||
|
|
||||||
|
*Release date: Jan 23, 2018*
|
||||||
|
|
||||||
- **ADDED:** security requirements are now correctly set and can be customized; this should fix problems related
|
- **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`)
|
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
|
||||||
@@ -43,6 +92,8 @@ Changelog
|
|||||||
**1.2.2**
|
**1.2.2**
|
||||||
*********
|
*********
|
||||||
|
|
||||||
|
*Release date: Jan 12, 2018*
|
||||||
|
|
||||||
- **FIXED:** djangorestframework>=3.7.7 is now required because of breaking changes
|
- **FIXED:** djangorestframework>=3.7.7 is now required because of breaking changes
|
||||||
(:issue:`44`, :pr:`45`, thanks to :ghuser:`h-hirokawa`)
|
(:issue:`44`, :pr:`45`, thanks to :ghuser:`h-hirokawa`)
|
||||||
|
|
||||||
@@ -50,12 +101,16 @@ Changelog
|
|||||||
**1.2.1**
|
**1.2.1**
|
||||||
*********
|
*********
|
||||||
|
|
||||||
|
*Release date: Jan 12, 2018*
|
||||||
|
|
||||||
- Fixed deployment issues
|
- Fixed deployment issues
|
||||||
|
|
||||||
*********
|
*********
|
||||||
**1.2.0**
|
**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
|
- **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`)
|
longest common prefix of API urls (:issue:`37`, :pr:`42`)
|
||||||
- **IMPROVED:** removed inline scripts and styles from bundled HTML templates to increase CSP compatibility
|
- **IMPROVED:** removed inline scripts and styles from bundled HTML templates to increase CSP compatibility
|
||||||
@@ -67,6 +122,8 @@ Changelog
|
|||||||
**1.1.3**
|
**1.1.3**
|
||||||
*********
|
*********
|
||||||
|
|
||||||
|
*Release date: Jan 02, 2018*
|
||||||
|
|
||||||
- **FIXED:** schema view cache will now always ``Vary`` on the ``Cookie`` and ``Authentication`` (the
|
- **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
|
``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
|
authentication in ``swagger-ui`` and ``CurrentUserDefault`` values in the schema
|
||||||
@@ -75,6 +132,8 @@ Changelog
|
|||||||
**1.1.2**
|
**1.1.2**
|
||||||
*********
|
*********
|
||||||
|
|
||||||
|
*Release date: Jan 01, 2018*
|
||||||
|
|
||||||
- **IMPROVED:** updated ``swagger-ui`` to version 3.8.1
|
- **IMPROVED:** updated ``swagger-ui`` to version 3.8.1
|
||||||
- **IMPROVED:** removed some unneeded static files
|
- **IMPROVED:** removed some unneeded static files
|
||||||
|
|
||||||
@@ -82,6 +141,8 @@ Changelog
|
|||||||
**1.1.1**
|
**1.1.1**
|
||||||
*********
|
*********
|
||||||
|
|
||||||
|
*Release date: Dec 27, 2017*
|
||||||
|
|
||||||
- **ADDED:** :ref:`generate_swagger management command <management-command>`
|
- **ADDED:** :ref:`generate_swagger management command <management-command>`
|
||||||
(:issue:`29`, :pr:`31`, thanks to :ghuser:`beaugunderson`)
|
(:issue:`29`, :pr:`31`, thanks to :ghuser:`beaugunderson`)
|
||||||
- **FIXED:** fixed improper generation of ``\Z`` regex tokens - will now be repalced by ``$``
|
- **FIXED:** fixed improper generation of ``\Z`` regex tokens - will now be repalced by ``$``
|
||||||
@@ -90,6 +151,8 @@ Changelog
|
|||||||
**1.1.0**
|
**1.1.0**
|
||||||
*********
|
*********
|
||||||
|
|
||||||
|
*Release date: Dec 27, 2017*
|
||||||
|
|
||||||
- **ADDED:** added support for APIs versioned with ``URLPathVersioning`` or ``NamespaceVersioning``
|
- **ADDED:** added support for APIs versioned with ``URLPathVersioning`` or ``NamespaceVersioning``
|
||||||
- **ADDED:** added ability to recursively customize schema generation
|
- **ADDED:** added ability to recursively customize schema generation
|
||||||
:ref:`using pluggable inspector classes <custom-spec-inspectors>`
|
:ref:`using pluggable inspector classes <custom-spec-inspectors>`
|
||||||
@@ -105,6 +168,8 @@ Changelog
|
|||||||
**1.0.6**
|
**1.0.6**
|
||||||
*********
|
*********
|
||||||
|
|
||||||
|
*Release date: Dec 23, 2017*
|
||||||
|
|
||||||
- **FIXED:** Swagger UI "Try it out!" should now work with Django login
|
- **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`)
|
- **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
|
- **IMPROVED:** updated ``swagger-ui`` to version 3.8.0
|
||||||
@@ -116,6 +181,8 @@ Changelog
|
|||||||
**1.0.5**
|
**1.0.5**
|
||||||
*********
|
*********
|
||||||
|
|
||||||
|
*Release date: Dec 18, 2017*
|
||||||
|
|
||||||
- **FIXED:** fixed a crash caused by having read-only Serializers nested by reference
|
- **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
|
- **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>`_
|
`path() <https://docs.djangoproject.com/en/2.0/ref/urls/#django.urls.path>`_
|
||||||
@@ -127,6 +194,8 @@ Changelog
|
|||||||
**1.0.4**
|
**1.0.4**
|
||||||
*********
|
*********
|
||||||
|
|
||||||
|
*Release date: Dec 16, 2017*
|
||||||
|
|
||||||
- **FIXED:** fixed improper generation of YAML references
|
- **FIXED:** fixed improper generation of YAML references
|
||||||
- **ADDED:** added ``query_serializer`` parameter to
|
- **ADDED:** added ``query_serializer`` parameter to
|
||||||
:func:`@swagger_auto_schema <.swagger_auto_schema>` (:issue:`16`, :pr:`17`)
|
:func:`@swagger_auto_schema <.swagger_auto_schema>` (:issue:`16`, :pr:`17`)
|
||||||
@@ -135,6 +204,8 @@ Changelog
|
|||||||
**1.0.3**
|
**1.0.3**
|
||||||
*********
|
*********
|
||||||
|
|
||||||
|
*Release date: Dec 15, 2017*
|
||||||
|
|
||||||
- **FIXED:** fixed bug that caused schema views returned from cache to fail (:issue:`14`)
|
- **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
|
- **FIXED:** disabled automatic generation of response schemas for form operations to avoid confusing errors caused by
|
||||||
attempting to shove file parameters into Schema objects
|
attempting to shove file parameters into Schema objects
|
||||||
@@ -143,4 +214,6 @@ Changelog
|
|||||||
**1.0.2**
|
**1.0.2**
|
||||||
*********
|
*********
|
||||||
|
|
||||||
|
*Release date: Dec 13, 2017*
|
||||||
|
|
||||||
- First published version
|
- First published version
|
||||||
|
|||||||
+27
-1
@@ -172,7 +172,11 @@ You can define some per-serializer options by adding a ``Meta`` class to your se
|
|||||||
Currently, the only option you can add here is
|
Currently, the only option you can add here is
|
||||||
|
|
||||||
* ``ref_name`` - a string which will be used as the model definition name for this serializer class; setting it to
|
* ``ref_name`` - a string which will be used as the model definition name for this serializer class; setting it to
|
||||||
``None`` will force the serializer to be generated as an inline model everywhere it is used
|
``None`` will force the serializer to be generated as an inline model everywhere it is used. If two serializers
|
||||||
|
have the same ``ref_name``, both their usages will be replaced with a reference to the same definition.
|
||||||
|
If this option is not specified, all serializers have an implicit name derived from their class name, minus any
|
||||||
|
``Serializer`` suffix (e.g. ``UserSerializer`` -> ``User``, ``SerializerWithSuffix`` -> ``SerializerWithSuffix``)
|
||||||
|
|
||||||
|
|
||||||
*************************
|
*************************
|
||||||
Subclassing and extending
|
Subclassing and extending
|
||||||
@@ -301,3 +305,25 @@ A second example, of a :class:`~.inspectors.FieldInspector` that removes the ``t
|
|||||||
This means that you should generally avoid view or method-specific ``FieldInspector``\ s if you are dealing with
|
This means that you should generally avoid view or method-specific ``FieldInspector``\ s if you are dealing with
|
||||||
references (a.k.a named models), because you can never know which view will be the first to generate the schema
|
references (a.k.a named models), because you can never know which view will be the first to generate the schema
|
||||||
for a given serializer.
|
for a given serializer.
|
||||||
|
|
||||||
|
**IMPORTANT:** nested fields on ``ModelSerializer``\ s that are generated from model ``ForeignKeys`` will always be
|
||||||
|
output by value. If you want the by-reference behaviour you have to explictly set the serializer class of nested
|
||||||
|
fields instead of letting ``ModelSerializer`` generate one automatically; for example:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
class OneSerializer(serializers.ModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = SomeModel
|
||||||
|
fields = ('id',)
|
||||||
|
|
||||||
|
|
||||||
|
class AnotherSerializer(serializers.ModelSerializer):
|
||||||
|
chilf = OneSerializer()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = SomeParentModel
|
||||||
|
fields = ('id', 'child')
|
||||||
|
|
||||||
|
Another caveat that stems from this is that any serializer named "``NestedSerializer``" will be forced inline
|
||||||
|
unless it has a ``ref_name`` set explicitly.
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ openapi_codec>=1.3.2
|
|||||||
ruamel.yaml>=0.15.34
|
ruamel.yaml>=0.15.34
|
||||||
inflection>=0.3.1
|
inflection>=0.3.1
|
||||||
future>=0.16.0
|
future>=0.16.0
|
||||||
six>=1.11.0
|
six>=1.10.0
|
||||||
uritemplate>=3.0.0
|
uritemplate>=3.0.0
|
||||||
|
|
||||||
djangorestframework>=3.7.7
|
djangorestframework>=3.7.7
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import copy
|
||||||
import logging
|
import logging
|
||||||
import re
|
import re
|
||||||
from collections import OrderedDict, defaultdict
|
from collections import OrderedDict, defaultdict
|
||||||
@@ -388,7 +389,7 @@ class OpenAPISchemaGenerator(object):
|
|||||||
if method in overrides:
|
if method in overrides:
|
||||||
overrides = overrides[method]
|
overrides = overrides[method]
|
||||||
|
|
||||||
return overrides
|
return copy.deepcopy(overrides)
|
||||||
|
|
||||||
def get_path_parameters(self, path, view_cls):
|
def get_path_parameters(self, path, view_cls):
|
||||||
"""Return a list of Parameter instances corresponding to any templated path variables.
|
"""Return a list of Parameter instances corresponding to any templated path variables.
|
||||||
@@ -405,8 +406,8 @@ class OpenAPISchemaGenerator(object):
|
|||||||
for variable in uritemplate.variables(path):
|
for variable in uritemplate.variables(path):
|
||||||
model, model_field = get_queryset_field(queryset, variable)
|
model, model_field = get_queryset_field(queryset, variable)
|
||||||
attrs = get_basic_type_info(model_field) or {'type': openapi.TYPE_STRING}
|
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:
|
if getattr(view_cls, 'lookup_field', None) == variable and attrs['type'] == openapi.TYPE_STRING:
|
||||||
attrs['pattern'] = view_cls.lookup_value_regex
|
attrs['pattern'] = getattr(view_cls, 'lookup_value_regex', attrs.get('pattern', None))
|
||||||
|
|
||||||
if model_field and model_field.help_text:
|
if model_field and model_field.help_text:
|
||||||
description = force_text(model_field.help_text)
|
description = force_text(model_field.help_text)
|
||||||
|
|||||||
@@ -234,7 +234,8 @@ class FieldInspector(BaseInspector):
|
|||||||
if default is not None:
|
if default is not None:
|
||||||
instance_kwargs['default'] = default
|
instance_kwargs['default'] = default
|
||||||
|
|
||||||
instance_kwargs.setdefault('title', title)
|
if instance_kwargs.get('type', None) != openapi.TYPE_ARRAY:
|
||||||
|
instance_kwargs.setdefault('title', title)
|
||||||
instance_kwargs.setdefault('description', description)
|
instance_kwargs.setdefault('description', description)
|
||||||
instance_kwargs.update(kwargs)
|
instance_kwargs.update(kwargs)
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import logging
|
||||||
import operator
|
import operator
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
@@ -12,6 +13,8 @@ from ..errors import SwaggerGenerationError
|
|||||||
from ..utils import decimal_as_float, filter_none
|
from ..utils import decimal_as_float, filter_none
|
||||||
from .base import FieldInspector, NotHandled, SerializerInspector
|
from .base import FieldInspector, NotHandled, SerializerInspector
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class InlineSerializerInspector(SerializerInspector):
|
class InlineSerializerInspector(SerializerInspector):
|
||||||
"""Provides serializer conversions using :meth:`.FieldInspector.field_to_swagger_object`."""
|
"""Provides serializer conversions using :meth:`.FieldInspector.field_to_swagger_object`."""
|
||||||
@@ -54,10 +57,14 @@ class InlineSerializerInspector(SerializerInspector):
|
|||||||
|
|
||||||
serializer = field
|
serializer = field
|
||||||
serializer_meta = getattr(serializer, 'Meta', None)
|
serializer_meta = getattr(serializer, 'Meta', None)
|
||||||
|
serializer_name = type(serializer).__name__
|
||||||
if hasattr(serializer_meta, 'ref_name'):
|
if hasattr(serializer_meta, 'ref_name'):
|
||||||
ref_name = serializer_meta.ref_name
|
ref_name = serializer_meta.ref_name
|
||||||
|
elif serializer_name == 'NestedSerializer' and isinstance(serializer, serializers.ModelSerializer):
|
||||||
|
logger.debug("Forcing inline output for ModelSerializer named 'NestedSerializer': " + str(serializer))
|
||||||
|
ref_name = None
|
||||||
else:
|
else:
|
||||||
ref_name = type(serializer).__name__
|
ref_name = serializer_name
|
||||||
if ref_name.endswith('Serializer'):
|
if ref_name.endswith('Serializer'):
|
||||||
ref_name = ref_name[:-len('Serializer')]
|
ref_name = ref_name[:-len('Serializer')]
|
||||||
|
|
||||||
@@ -77,11 +84,17 @@ class InlineSerializerInspector(SerializerInspector):
|
|||||||
if child.required:
|
if child.required:
|
||||||
required.append(property_name)
|
required.append(property_name)
|
||||||
|
|
||||||
return SwaggerType(
|
result = SwaggerType(
|
||||||
type=openapi.TYPE_OBJECT,
|
type=openapi.TYPE_OBJECT,
|
||||||
properties=properties,
|
properties=properties,
|
||||||
required=required or None,
|
required=required or None,
|
||||||
)
|
)
|
||||||
|
if not ref_name:
|
||||||
|
# on an inline model, the title is derived from the field name
|
||||||
|
# but is visually displayed like the model named, which is confusing
|
||||||
|
# it is better to just remove title from inline models
|
||||||
|
del result.title
|
||||||
|
return result
|
||||||
|
|
||||||
if not ref_name or not use_references:
|
if not ref_name or not use_references:
|
||||||
return make_schema_definition()
|
return make_schema_definition()
|
||||||
@@ -348,7 +361,10 @@ def get_basic_type_info(field):
|
|||||||
else: # pragma: no cover
|
else: # pragma: no cover
|
||||||
return None
|
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)
|
limits = find_limits(field)
|
||||||
|
|
||||||
result = OrderedDict([
|
result = OrderedDict([
|
||||||
@@ -381,16 +397,27 @@ class ChoiceFieldInspector(FieldInspector):
|
|||||||
def field_to_swagger_object(self, field, swagger_object_type, use_references, **kwargs):
|
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)
|
SwaggerType, ChildSwaggerType = self._get_partial_types(field, swagger_object_type, use_references, **kwargs)
|
||||||
|
|
||||||
if isinstance(field, serializers.MultipleChoiceField):
|
if isinstance(field, serializers.ChoiceField):
|
||||||
return SwaggerType(
|
enum_type = openapi.TYPE_STRING
|
||||||
type=openapi.TYPE_ARRAY,
|
|
||||||
items=ChildSwaggerType(
|
# for ModelSerializer, try to infer the type from the associated model field
|
||||||
type=openapi.TYPE_STRING,
|
serializer = get_parent_serializer(field)
|
||||||
enum=list(field.choices.keys())
|
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=enum_type, enum=list(field.choices.keys()))
|
||||||
return SwaggerType(type=openapi.TYPE_STRING, enum=list(field.choices.keys()))
|
|
||||||
|
|
||||||
return NotHandled
|
return NotHandled
|
||||||
|
|
||||||
|
|||||||
@@ -221,7 +221,7 @@ class SwaggerAutoSchema(ViewInspector):
|
|||||||
)
|
)
|
||||||
elif isinstance(serializer, openapi.Response):
|
elif isinstance(serializer, openapi.Response):
|
||||||
response = serializer
|
response = serializer
|
||||||
if not isinstance(response.schema, openapi.Schema.OR_REF):
|
if hasattr(response, 'schema') and not isinstance(response.schema, openapi.Schema.OR_REF):
|
||||||
serializer = force_serializer_instance(response.schema)
|
serializer = force_serializer_instance(response.schema)
|
||||||
response.schema = self.serializer_to_schema(serializer)
|
response.schema = self.serializer_to_schema(serializer)
|
||||||
elif isinstance(serializer, openapi.Schema.OR_REF):
|
elif isinstance(serializer, openapi.Schema.OR_REF):
|
||||||
|
|||||||
@@ -396,6 +396,7 @@ class Parameter(SwaggerDict):
|
|||||||
self.enum = enum
|
self.enum = enum
|
||||||
self.pattern = pattern
|
self.pattern = pattern
|
||||||
self.items = items
|
self.items = items
|
||||||
|
self.default = default
|
||||||
self._insert_extras__()
|
self._insert_extras__()
|
||||||
if self['in'] == IN_PATH:
|
if self['in'] == IN_PATH:
|
||||||
# path parameters must always be required
|
# path parameters must always be required
|
||||||
|
|||||||
@@ -11,10 +11,15 @@ from rest_framework.views import APIView
|
|||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
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,
|
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;
|
: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, and can be set to ``None`` to prevent this operation from being generated
|
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'``.
|
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
|
A Schema or SchemaRef is not valid if this request consumes form-data, because ``form`` and ``body`` parameters
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 2.0.1 on 2018-02-26 18:32
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('articles', '0001_initial'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='article',
|
||||||
|
name='article_type',
|
||||||
|
field=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),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -8,5 +8,9 @@ class Article(models.Model):
|
|||||||
date_created = models.DateTimeField(auto_now_add=True)
|
date_created = models.DateTimeField(auto_now_add=True)
|
||||||
date_modified = models.DateTimeField(auto_now=True)
|
date_modified = models.DateTimeField(auto_now=True)
|
||||||
author = models.ForeignKey('auth.User', related_name='articles', on_delete=models.CASCADE)
|
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)
|
cover = models.ImageField(upload_to='article/original/', blank=True)
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ class ArticleSerializer(serializers.ModelSerializer):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = Article
|
model = Article
|
||||||
fields = ('title', 'author', 'body', 'slug', 'date_created', 'date_modified',
|
fields = ('title', 'author', 'body', 'slug', 'date_created', 'date_modified',
|
||||||
'references', 'uuid', 'cover', 'cover_name')
|
'references', 'uuid', 'cover', 'cover_name', 'article_type')
|
||||||
read_only_fields = ('date_created', 'date_modified',
|
read_only_fields = ('date_created', 'date_modified',
|
||||||
'references', 'uuid', 'cover_name')
|
'references', 'uuid', 'cover_name')
|
||||||
lookup_field = 'slug'
|
lookup_field = 'slug'
|
||||||
@@ -31,7 +31,7 @@ class ArticleSerializer(serializers.ModelSerializer):
|
|||||||
|
|
||||||
|
|
||||||
class ImageUploadSerializer(serializers.Serializer):
|
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(
|
image_styles = serializers.ListSerializer(
|
||||||
child=serializers.ChoiceField(choices=['wide', 'tall', 'thumb', 'social']),
|
child=serializers.ChoiceField(choices=['wide', 'tall', 'thumb', 'social']),
|
||||||
help_text="Parameter with Items"
|
help_text="Parameter with Items"
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
from djangorestframework_camel_case.parser import CamelCaseJSONParser
|
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, status
|
||||||
from rest_framework.parsers import FormParser
|
from rest_framework.parsers import FormParser
|
||||||
|
|
||||||
from drf_yasg import openapi
|
from drf_yasg import openapi
|
||||||
@@ -62,8 +62,12 @@ class SnippetDetail(generics.RetrieveUpdateDestroyAPIView):
|
|||||||
type=openapi.TYPE_INTEGER,
|
type=openapi.TYPE_INTEGER,
|
||||||
description="path parameter override",
|
description="path parameter override",
|
||||||
required=True
|
required=True
|
||||||
|
),
|
||||||
|
], responses={
|
||||||
|
status.HTTP_204_NO_CONTENT: openapi.Response(
|
||||||
|
description="This should not crash"
|
||||||
)
|
)
|
||||||
])
|
})
|
||||||
def delete(self, request, *args, **kwargs):
|
def delete(self, request, *args, **kwargs):
|
||||||
"""delete method docstring"""
|
"""delete method docstring"""
|
||||||
return super(SnippetDetail, self).patch(request, *args, **kwargs)
|
return super(SnippetDetail, self).patch(request, *args, **kwargs)
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ INSTALLED_APPS = [
|
|||||||
'snippets',
|
'snippets',
|
||||||
'users',
|
'users',
|
||||||
'articles',
|
'articles',
|
||||||
|
'todo',
|
||||||
]
|
]
|
||||||
|
|
||||||
MIDDLEWARE = [
|
MIDDLEWARE = [
|
||||||
|
|||||||
@@ -62,5 +62,6 @@ urlpatterns = [
|
|||||||
url(r'^snippets/', include('snippets.urls')),
|
url(r'^snippets/', include('snippets.urls')),
|
||||||
url(r'^articles/', include('articles.urls')),
|
url(r'^articles/', include('articles.urls')),
|
||||||
url(r'^users/', include('users.urls')),
|
url(r'^users/', include('users.urls')),
|
||||||
|
url(r'^todo/', include('todo.urls')),
|
||||||
url(r'^plain/', plain_view),
|
url(r'^plain/', plain_view),
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.11 on 2018-02-21 23:26
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
initial = True
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Todo',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('title', models.CharField(max_length=50)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='TodoAnother',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('title', models.CharField(max_length=50)),
|
||||||
|
('todo', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='todo.Todo')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='TodoYetAnother',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('title', models.CharField(max_length=50)),
|
||||||
|
('todo', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='todo.TodoAnother')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
from django.db import models
|
||||||
|
|
||||||
|
|
||||||
|
class Todo(models.Model):
|
||||||
|
title = models.CharField(max_length=50)
|
||||||
|
|
||||||
|
|
||||||
|
class TodoAnother(models.Model):
|
||||||
|
todo = models.ForeignKey(Todo, on_delete=models.CASCADE)
|
||||||
|
title = models.CharField(max_length=50)
|
||||||
|
|
||||||
|
|
||||||
|
class TodoYetAnother(models.Model):
|
||||||
|
todo = models.ForeignKey(TodoAnother, on_delete=models.CASCADE)
|
||||||
|
title = models.CharField(max_length=50)
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
from rest_framework import serializers
|
||||||
|
|
||||||
|
from .models import Todo, TodoAnother, TodoYetAnother
|
||||||
|
|
||||||
|
|
||||||
|
class TodoSerializer(serializers.ModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = Todo
|
||||||
|
fields = ('title',)
|
||||||
|
|
||||||
|
|
||||||
|
class TodoAnotherSerializer(serializers.ModelSerializer):
|
||||||
|
todo = TodoSerializer()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = TodoAnother
|
||||||
|
fields = ('title', 'todo')
|
||||||
|
|
||||||
|
|
||||||
|
class TodoYetAnotherSerializer(serializers.ModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = TodoYetAnother
|
||||||
|
fields = ('title', 'todo')
|
||||||
|
depth = 2
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
from rest_framework import routers
|
||||||
|
|
||||||
|
from todo import views
|
||||||
|
|
||||||
|
router = routers.DefaultRouter()
|
||||||
|
router.register(r'', views.TodoViewSet)
|
||||||
|
router.register(r'another', views.TodoAnotherViewSet)
|
||||||
|
router.register(r'yetanother', views.TodoYetAnotherViewSet)
|
||||||
|
|
||||||
|
urlpatterns = router.urls
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
from rest_framework import viewsets
|
||||||
|
|
||||||
|
from .models import Todo, TodoAnother, TodoYetAnother
|
||||||
|
from .serializer import TodoAnotherSerializer, TodoSerializer, TodoYetAnotherSerializer
|
||||||
|
|
||||||
|
|
||||||
|
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()
|
||||||
|
serializer_class = TodoAnotherSerializer
|
||||||
|
|
||||||
|
|
||||||
|
class TodoYetAnotherViewSet(viewsets.ReadOnlyModelViewSet):
|
||||||
|
queryset = TodoYetAnother.objects.all()
|
||||||
|
serializer_class = TodoYetAnotherSerializer
|
||||||
+170
-9
@@ -198,9 +198,10 @@ paths:
|
|||||||
- name: what_am_i_doing
|
- name: what_am_i_doing
|
||||||
in: formData
|
in: formData
|
||||||
description: test
|
description: test
|
||||||
required: true
|
required: false
|
||||||
type: string
|
type: string
|
||||||
pattern: ^69$
|
pattern: ^69$
|
||||||
|
default: '69'
|
||||||
- name: image_styles
|
- name: image_styles
|
||||||
in: formData
|
in: formData
|
||||||
description: Parameter with Items
|
description: Parameter with Items
|
||||||
@@ -330,7 +331,7 @@ paths:
|
|||||||
type: integer
|
type: integer
|
||||||
responses:
|
responses:
|
||||||
'204':
|
'204':
|
||||||
description: ''
|
description: This should not crash
|
||||||
tags:
|
tags:
|
||||||
- snippets
|
- snippets
|
||||||
parameters:
|
parameters:
|
||||||
@@ -339,6 +340,105 @@ paths:
|
|||||||
description: A unique integer value identifying this snippet.
|
description: A unique integer value identifying this snippet.
|
||||||
required: true
|
required: true
|
||||||
type: integer
|
type: integer
|
||||||
|
/todo/:
|
||||||
|
get:
|
||||||
|
operationId: todo_list
|
||||||
|
description: ''
|
||||||
|
parameters: []
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: ''
|
||||||
|
schema:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/Todo'
|
||||||
|
tags:
|
||||||
|
- todo
|
||||||
|
parameters: []
|
||||||
|
/todo/another/:
|
||||||
|
get:
|
||||||
|
operationId: todo_another_list
|
||||||
|
description: ''
|
||||||
|
parameters: []
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: ''
|
||||||
|
schema:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/TodoAnother'
|
||||||
|
tags:
|
||||||
|
- todo
|
||||||
|
parameters: []
|
||||||
|
/todo/another/{id}/:
|
||||||
|
get:
|
||||||
|
operationId: todo_another_read
|
||||||
|
description: ''
|
||||||
|
parameters: []
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: ''
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/TodoAnother'
|
||||||
|
tags:
|
||||||
|
- todo
|
||||||
|
parameters:
|
||||||
|
- name: id
|
||||||
|
in: path
|
||||||
|
description: A unique integer value identifying this todo another.
|
||||||
|
required: true
|
||||||
|
type: integer
|
||||||
|
/todo/yetanother/:
|
||||||
|
get:
|
||||||
|
operationId: todo_yetanother_list
|
||||||
|
description: ''
|
||||||
|
parameters: []
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: ''
|
||||||
|
schema:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/TodoYetAnother'
|
||||||
|
tags:
|
||||||
|
- todo
|
||||||
|
parameters: []
|
||||||
|
/todo/yetanother/{id}/:
|
||||||
|
get:
|
||||||
|
operationId: todo_yetanother_read
|
||||||
|
description: ''
|
||||||
|
parameters: []
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: ''
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/TodoYetAnother'
|
||||||
|
tags:
|
||||||
|
- todo
|
||||||
|
parameters:
|
||||||
|
- name: id
|
||||||
|
in: path
|
||||||
|
description: A unique integer value identifying this todo yet another.
|
||||||
|
required: true
|
||||||
|
type: integer
|
||||||
|
/todo/{id}/:
|
||||||
|
get:
|
||||||
|
operationId: todo_read
|
||||||
|
description: ''
|
||||||
|
parameters: []
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: ''
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/Todo'
|
||||||
|
tags:
|
||||||
|
- todo
|
||||||
|
parameters:
|
||||||
|
- name: id
|
||||||
|
in: path
|
||||||
|
description: A unique integer value identifying this todo.
|
||||||
|
required: true
|
||||||
|
type: integer
|
||||||
/users/:
|
/users/:
|
||||||
get:
|
get:
|
||||||
operationId: users_list
|
operationId: users_list
|
||||||
@@ -491,6 +591,16 @@ definitions:
|
|||||||
cover_name:
|
cover_name:
|
||||||
type: string
|
type: string
|
||||||
readOnly: true
|
readOnly: true
|
||||||
|
article_type:
|
||||||
|
description: IntegerField declared on model with choices=(...) and exposed
|
||||||
|
via ModelSerializer
|
||||||
|
type: integer
|
||||||
|
enum:
|
||||||
|
- 1
|
||||||
|
- 2
|
||||||
|
- 3
|
||||||
|
- 7
|
||||||
|
- 8
|
||||||
Project:
|
Project:
|
||||||
required:
|
required:
|
||||||
- projectName
|
- projectName
|
||||||
@@ -538,7 +648,6 @@ definitions:
|
|||||||
title: Linenos
|
title: Linenos
|
||||||
type: boolean
|
type: boolean
|
||||||
language:
|
language:
|
||||||
title: Language
|
|
||||||
description: Sample help text for language
|
description: Sample help text for language
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
@@ -983,7 +1092,6 @@ definitions:
|
|||||||
- zephir
|
- zephir
|
||||||
default: python
|
default: python
|
||||||
styles:
|
styles:
|
||||||
title: Styles
|
|
||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
type: string
|
type: string
|
||||||
@@ -1020,12 +1128,10 @@ definitions:
|
|||||||
default:
|
default:
|
||||||
- friendly
|
- friendly
|
||||||
lines:
|
lines:
|
||||||
title: Lines
|
|
||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
type: integer
|
type: integer
|
||||||
exampleProjects:
|
exampleProjects:
|
||||||
title: Example projects
|
|
||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
$ref: '#/definitions/Project'
|
$ref: '#/definitions/Project'
|
||||||
@@ -1047,6 +1153,64 @@ definitions:
|
|||||||
format: decimal
|
format: decimal
|
||||||
default: 0.0
|
default: 0.0
|
||||||
minimum: 0.0
|
minimum: 0.0
|
||||||
|
Todo:
|
||||||
|
required:
|
||||||
|
- title
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
title:
|
||||||
|
title: Title
|
||||||
|
type: string
|
||||||
|
maxLength: 50
|
||||||
|
TodoAnother:
|
||||||
|
required:
|
||||||
|
- title
|
||||||
|
- todo
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
title:
|
||||||
|
title: Title
|
||||||
|
type: string
|
||||||
|
maxLength: 50
|
||||||
|
todo:
|
||||||
|
$ref: '#/definitions/Todo'
|
||||||
|
TodoYetAnother:
|
||||||
|
required:
|
||||||
|
- title
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
title:
|
||||||
|
title: Title
|
||||||
|
type: string
|
||||||
|
maxLength: 50
|
||||||
|
todo:
|
||||||
|
required:
|
||||||
|
- title
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
title: ID
|
||||||
|
type: integer
|
||||||
|
readOnly: true
|
||||||
|
title:
|
||||||
|
title: Title
|
||||||
|
type: string
|
||||||
|
maxLength: 50
|
||||||
|
todo:
|
||||||
|
required:
|
||||||
|
- title
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
title: ID
|
||||||
|
type: integer
|
||||||
|
readOnly: true
|
||||||
|
title:
|
||||||
|
title: Title
|
||||||
|
type: string
|
||||||
|
maxLength: 50
|
||||||
|
readOnly: true
|
||||||
|
readOnly: true
|
||||||
UserSerializerrr:
|
UserSerializerrr:
|
||||||
required:
|
required:
|
||||||
- username
|
- username
|
||||||
@@ -1071,13 +1235,11 @@ definitions:
|
|||||||
format: email
|
format: email
|
||||||
maxLength: 254
|
maxLength: 254
|
||||||
articles:
|
articles:
|
||||||
title: Articles
|
|
||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
type: integer
|
type: integer
|
||||||
uniqueItems: true
|
uniqueItems: true
|
||||||
snippets:
|
snippets:
|
||||||
title: Snippets
|
|
||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
type: integer
|
type: integer
|
||||||
@@ -1095,7 +1257,6 @@ definitions:
|
|||||||
format: date
|
format: date
|
||||||
readOnly: true
|
readOnly: true
|
||||||
article_slugs:
|
article_slugs:
|
||||||
title: Article slugs
|
|
||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
type: string
|
type: string
|
||||||
|
|||||||
@@ -46,3 +46,9 @@ def test_noop_inspectors(swagger_settings, mock_schema_request, codec_json, refe
|
|||||||
json_bytes = codec_json.encode(swagger)
|
json_bytes = codec_json.encode(swagger)
|
||||||
swagger_dict = json.loads(json_bytes.decode('utf-8'), object_pairs_hook=OrderedDict)
|
swagger_dict = json.loads(json_bytes.decode('utf-8'), object_pairs_hook=OrderedDict)
|
||||||
compare_schemas(swagger_dict, reference_schema)
|
compare_schemas(swagger_dict, reference_schema)
|
||||||
|
|
||||||
|
|
||||||
|
def test_no_nested_model(swagger_dict):
|
||||||
|
# ForeignKey models in deep ModelViewSets might wrongly be labeled as 'Nested' in the definitions section
|
||||||
|
# see https://github.com/axnsan12/drf-yasg/issues/59
|
||||||
|
assert 'Nested' not in swagger_dict['definitions']
|
||||||
|
|||||||
@@ -2,11 +2,14 @@ import json
|
|||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
from rest_framework import routers, serializers, viewsets
|
||||||
|
from rest_framework.response import Response
|
||||||
|
|
||||||
from drf_yasg import codecs, openapi
|
from drf_yasg import codecs, openapi
|
||||||
from drf_yasg.codecs import yaml_sane_load
|
from drf_yasg.codecs import yaml_sane_load
|
||||||
from drf_yasg.errors import SwaggerGenerationError
|
from drf_yasg.errors import SwaggerGenerationError
|
||||||
from drf_yasg.generators import OpenAPISchemaGenerator
|
from drf_yasg.generators import OpenAPISchemaGenerator
|
||||||
|
from drf_yasg.utils import swagger_auto_schema
|
||||||
|
|
||||||
|
|
||||||
def test_schema_is_valid(swagger, codec_yaml):
|
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)
|
swagger = generator.get_schema(mock_schema_request, public=True)
|
||||||
assert swagger['security'] == []
|
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"
|
||||||
|
|||||||
Reference in New Issue
Block a user