Giter Site home page Giter Site logo

axnsan12 / drf-yasg Goto Github PK

View Code? Open in Web Editor NEW
3.3K 31.0 433.0 22.69 MB

Automated generation of real Swagger/OpenAPI 2.0 schemas from Django REST Framework code.

Home Page: https://drf-yasg.readthedocs.io/en/stable/

License: Other

Python 65.46% JavaScript 32.52% HTML 1.59% Shell 0.17% CSS 0.24% Procfile 0.02%
django-rest-swagger django-rest-framework django swagger swagger-codegen swagger-ui redoc documentation-generator coreapi openapi

drf-yasg's Introduction

drf-yasg - Yet another Swagger generator

GitHub Workflow Status   Codecov   ReadTheDocs   PyPI

Generate real Swagger/OpenAPI 2.0 specifications from a Django Rest Framework API.

Compatible with

  • Django Rest Framework: 3.10, 3.11, 3.12, 3.13, 3.14
  • Django: 2.2, 3.0, 3.1, 3.2, 4.0, 4.1
  • Python: 3.6, 3.7, 3.8, 3.9, 3.10, 3.11

Only the latest patch version of each major.minor series of Python, Django and Django REST Framework is supported.

Only the latest version of drf-yasg is supported. Support of old versions is dropped immediately with the release of a new version. Please do not create issues before upgrading to the latest release available at the time. Regression reports are accepted and will be resolved with a new release as quickly as possible. Removed features will usually go through a deprecation cycle of a few minor releases.

Resources:

Heroku deploy button

OpenAPI 3.0 note

If you are looking to add Swagger/OpenAPI support to a new project you might want to take a look at drf-spectacular, which is an actively maintained new library that shares most of the goals of this project, while working with OpenAPI 3.0 schemas.

OpenAPI 3.0 provides a lot more flexibility than 2.0 in the types of API that can be described. drf-yasg is unlikely to soon, if ever, get support for OpenAPI 3.0.

Features

  • full support for nested Serializers and Schemas
  • response schemas and descriptions
  • model definitions compatible with codegen tools
  • customization hooks at all points in the spec generation process
  • JSON and YAML format for spec
  • bundles latest version of swagger-ui and redoc for viewing the generated documentation
  • schema view is cacheable out of the box
  • generated Swagger schema can be automatically validated by swagger-spec-validator
  • supports Django REST Framework API versioning with URLPathVersioning and NamespaceVersioning; other DRF or custom versioning schemes are not currently supported

Fully nested request and response schemas.

Fully nested request and response schemas.

Choose between redoc and swagger-ui.

Choose between redoc and swagger-ui.

Real Model definitions.

Real Model definitions.

Table of contents

Usage

0. Installation

The preferred installation method is directly from pypi:

pip install -U drf-yasg

Additionally, if you want to use the built-in validation mechanisms (see 4. Validation), you need to install some extra requirements:

pip install -U drf-yasg[validation]

1. Quickstart

In settings.py:

INSTALLED_APPS = [
   ...
   'django.contrib.staticfiles',  # required for serving swagger ui's css/js files
   'drf_yasg',
   ...
]

In urls.py:

...
from django.urls import re_path
from rest_framework import permissions
from drf_yasg.views import get_schema_view
from drf_yasg import openapi

...

schema_view = get_schema_view(
   openapi.Info(
      title="Snippets API",
      default_version='v1',
      description="Test description",
      terms_of_service="https://www.google.com/policies/terms/",
      contact=openapi.Contact(email="[email protected]"),
      license=openapi.License(name="BSD License"),
   ),
   public=True,
   permission_classes=(permissions.AllowAny,),
)

urlpatterns = [
   path('swagger<format>/', schema_view.without_ui(cache_timeout=0), name='schema-json'),
   path('swagger/', schema_view.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui'),
   path('redoc/', schema_view.with_ui('redoc', cache_timeout=0), name='schema-redoc'),
   ...
]

This exposes 4 endpoints:

  • A JSON view of your API specification at /swagger.json
  • A YAML view of your API specification at /swagger.yaml
  • A swagger-ui view of your API specification at /swagger/
  • A ReDoc view of your API specification at /redoc/

2. Configuration

a. get_schema_view parameters

  • info - Swagger API Info object; if omitted, defaults to DEFAULT_INFO
  • url - API base url; if left blank will be deduced from the location the view is served at
  • patterns - passed to SchemaGenerator
  • urlconf - passed to SchemaGenerator
  • public - if False, includes only endpoints the current user has access to
  • validators - a list of validator names to apply on the generated schema; only ssv is currently supported
  • generator_class - schema generator class to use; should be a subclass of OpenAPISchemaGenerator
  • authentication_classes - authentication classes for the schema view itself
  • permission_classes - permission classes for the schema view itself

b. SchemaView options

  • SchemaView.with_ui(renderer, cache_timeout, cache_kwargs) - get a view instance using the specified UI renderer; one of swagger, redoc
  • SchemaView.without_ui(cache_timeout, cache_kwargs) - get a view instance with no UI renderer; same as as_cached_view with no kwargs
  • SchemaView.as_cached_view(cache_timeout, cache_kwargs, **initkwargs) - same as as_view, but with optional caching
  • you can, of course, call as_view as usual

All of the first 3 methods take two optional arguments, cache_timeout and cache_kwargs; if present, these are passed on to Django’s cached_page decorator in order to enable caching on the resulting view. See 3. Caching.

c. SWAGGER_SETTINGS and REDOC_SETTINGS

Additionally, you can include some more settings in your settings.py file. See https://drf-yasg.readthedocs.io/en/stable/settings.html for details.

3. Caching

Since the schema does not usually change during the lifetime of the django process, there is out of the box support for caching the schema view in-memory, with some sane defaults:

  • caching is enabled by the cache_page decorator, using the default Django cache backend, can be changed using the cache_kwargs argument
  • HTTP caching of the response is blocked to avoid confusing situations caused by being shown stale schemas
  • the cached schema varies on the Cookie and Authorization HTTP headers to enable filtering of visible endpoints according to the authentication credentials of each user; note that this means that every user accessing the schema will have a separate schema cached in memory.

4. Validation

Given the numerous methods to manually customize the generated schema, it makes sense to validate the result to ensure it still conforms to OpenAPI 2.0. To this end, validation is provided at the generation point using python swagger libraries, and can be activated by passing validators=['ssv'] to get_schema_view; if the generated schema is not valid, a SwaggerValidationError is raised by the handling codec.

Warning: This internal validation can slow down your server. Caching can mitigate the speed impact of validation.

The provided validation will catch syntactic errors, but more subtle violations of the spec might slip by them. To ensure compatibility with code generation tools, it is recommended to also employ one or more of the following methods:

swagger-ui validation badge

Online

If your schema is publicly accessible, swagger-ui will automatically validate it against the official swagger online validator and display the result in the bottom-right validation badge.

Offline

If your schema is not accessible from the internet, you can run a local copy of swagger-validator and set the VALIDATOR_URL accordingly:

SWAGGER_SETTINGS = {
    ...
    'VALIDATOR_URL': 'http://localhost:8189',
    ...
}
$ docker run --name swagger-validator -d -p 8189:8080 --add-host test.local:10.0.75.1 swaggerapi/swagger-validator
84dabd52ba967c32ae6b660934fa6a429ca6bc9e594d56e822a858b57039c8a2
$ curl http://localhost:8189/debug?url=http://test.local:8002/swagger/?format=openapi
{}

Using swagger-cli

https://www.npmjs.com/package/swagger-cli

$ npm install -g swagger-cli
[...]
$ swagger-cli validate http://test.local:8002/swagger.yaml
http://test.local:8002/swagger.yaml is valid

Manually on editor.swagger.io

Importing the generated spec into https://editor.swagger.io/ will automatically trigger validation on it. This method is currently the only way to get both syntactic and semantic validation on your specification. The other validators only provide JSON schema-level validation, but miss things like duplicate operation names, improper content types, etc

5. Code generation

You can use the specification outputted by this library together with swagger-codegen to generate client code in your language of choice:

$ docker run --rm -v ${PWD}:/local swaggerapi/swagger-codegen-cli generate -i /local/tests/reference.yaml -l javascript -o /local/.codegen/js

See the github page linked above for more details.

6. Example project

For additional usage examples, you can take a look at the test project in the testproj directory:

$ git clone https://github.com/axnsan12/drf-yasg.git
$ cd drf-yasg
$ virtualenv venv
$ source venv/bin/activate
(venv) $ cd testproj
(venv) $ python -m pip install -U pip setuptools
(venv) $ pip install -U -r requirements.txt
(venv) $ python manage.py migrate
(venv) $ python manage.py runserver
(venv) $ firefox localhost:8000/swagger/

Third-party integrations

djangorestframework-camel-case

Integration with djangorestframework-camel-case is provided out of the box - if you have djangorestframework-camel-case installed and your APIView uses CamelCaseJSONParser or CamelCaseJSONRenderer, all property names will be converted to camelCase by default.

djangorestframework-recursive

Integration with djangorestframework-recursive is provided out of the box - if you have djangorestframework-recursive installed.

drf-extra-fields

Integration with drf-extra-fields has a problem with Base64 fields. The drf-yasg will generate Base64 file or image fields as Readonly and not required. Here is a workaround code for display the Base64 fields correctly.

class PDFBase64FileField(Base64FileField):
    ALLOWED_TYPES = ['pdf']

    class Meta:
        swagger_schema_fields = {
            'type': 'string',
            'title': 'File Content',
            'description': 'Content of the file base64 encoded',
            'read_only': False  # <-- FIX
        }

    def get_file_extension(self, filename, decoded_file):
        try:
            PyPDF2.PdfFileReader(io.BytesIO(decoded_file))
        except PyPDF2.utils.PdfReadError as e:
            logger.warning(e)
        else:
            return 'pdf'

drf-yasg's People

Contributors

amoki avatar axnsan12 avatar blueyed avatar brianhelba avatar bryant1410 avatar carlfarrington avatar elliott-omosheye avatar etene avatar grumbling-tom avatar hysios avatar jamim avatar joellefkowitz avatar johnthagen avatar ko-pp avatar mofr avatar mpwang avatar pahaz avatar paulway avatar petrdlouhy avatar quozd avatar rafalolszewski94 avatar rsichnyi avatar terencehonles avatar therefromhere avatar ticosax avatar tijuca avatar tuky avatar xiaohanzhang avatar ychab avatar ymyzk avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

drf-yasg's Issues

Strange Behavior of class docstrings

Hi,

i get to see a strange behavior when it comes to the display of a class docstring in the swagger UI.

The first Docstring where it occurs looks like this :
"""
This is a Filter Endpoint. It allows to filter it's Resource according to the
databasefields an their values.
Providing a field and it's value to filter by just use the common GET syntax.

This Means :
    * url\?\<first_fieldname\>\=value1\&\<second_fieldname\>\=value2

Resource:
    * MineralType

Fields available for this Resource are:
    trivial_name, systematics, variety, minerals,
    mohs_scale, density, streak, normal_color,
    fracture, cleavage, lustre, chemical_formula,
    other, resource_mindat, resource_mineralienatlas

Note to filter for systematics one needs this 'translations':
   * EL = Elements
   * SF = Sulfides & Sulfosalts
   * HG = Halogenides
   * OH = Oxides and Hydroxides
   * CN = Carbonates and Nitrates
   * BR = Borates
   * SL = Sulfates
   * PV = Phosphates, Arsenates & Vanadates
   * SG = Silicates & Germanates
   * OC = Organic Compounds

"""

When this Docstring gets converted only the last enumeration gets rendered, by markdown, with the normal "dots"

like this.

BUT: The first two do not get converted which looks quite strange. Am I doing anything wrong here ?

The next point is : The blank lines are ignored ?!

Thanks in advance.

Provide consumes and produces at the root of the schema

Hey,

I would like to provide a list of types that the API consumes and produces at the root of the schema but I didn't find the way to achieve it...

The only way I found is to modify the get_schema method of OpenAPISchemaGenerator se it looks like this:

consumes = ['application/vnd.api+json']
produces = ['application/vnd.api+json']
return openapi.Swagger(
    info=self.info, paths=paths,
    _url=url, _prefix=prefix, _version=self.version,
    consumes=consumes, produces=produces,
    **dict(components)
)

Is there a way to achieve it without modifying the code, as a setting for example ?

Path parameter is not overrided

In the viewset, I add swagger_auto_schema decorator to destroy(self, request, pk=None) method, to override the id path parameter and add descriptions. But in the swagger UI, both of them are showing as path parameters.

@swagger_auto_schema(manual_parameters=[ openapi.Parameter('id', openapi.IN_PATH, description="Instrument Id to be removed from watchlist, if exist", type=openapi.TYPE_STRING, required=True), ])
image

JWT Bearer token authentication

I tried setting up my Access token in the Authorize field. It didn't work. When I check the curl request there is no header parameter for Authorization. Can someone tell me how to add Bearer Token Auth?

Detect url scheme when behind proxy

I'm running Django behind a nginx-ingress as a proxy which also handles HTTPS termination. This leads to the problem that drf-yasg autodetecs HTTP scheme as the connection between Django and nginx is HTTP. Therefore the generated swagger file points to the HTTP scheme and the swagger-ui is not correctly working as the browser blocks mixed content requests.

Mixed Content: The page at 'https://www.example.com/api/swagger/' was loaded over HTTPS, but requested an insecure resource 'http:///www.example.com/api/user/'. This request has been blocked; the content must be served over HTTPS.

I don't want to set DEFAULT_API_URL as Django runs inside a container and this container does not and should not know the hostnames it serves or the upstream protocol behind proxy.

This could be resolved by using the X-Forwarded-Proto HTTP header, which is set by the upstream proxy, in the OpenAPISchemaGenerator class.

TypeError: Decimal

getting this:
"TypeError: Object of type 'Decimal' is not JSON serializable"

for a model with such field:
rate = models.DecimalField(_('Rate'), max_digits=6, decimal_places=3, default=Decimal('0.0'), validators=[MinValueValidator(Decimal('0.0'))])

Cannot manually represent a Response without setting a schema?

Not sure if this is a bug, or if I'm just not reading the docs correctly:

I have a view that returns a '204 No Content' status, with a header that returns some relevant information, and no response body. I want to correctly represent this in my API documentation, so I'm doing something like this:

class MyView(APIView):
    @swagger_auto_schema(
        responses={
            "204": openapi.Response(
                description="Your request was accepted, perform status queries using the value of the X-Query-ID header for updates",
                 headers={
                     "X-Query-ID": {
                         "description": "The ID of the accepted query",
                         "type": openapi.TYPE_STRING
                     }
                 }
             )
         }
    )
    def post(self, request, ...):

However this fails with the following trace:

Traceback (most recent call last):
  File "/usr/local/lib/python3.6/site-packages/drf_yasg/openapi.py", line 99, in __getattr__
    return self[make_swagger_name(item)]
KeyError: 'schema'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/local/lib/python3.6/site-packages/django/core/handlers/exception.py", line 41, in inner
    response = get_response(request)
  File "/usr/local/lib/python3.6/site-packages/django/core/handlers/base.py", line 187, in _get_response
    response = self.process_exception_by_middleware(e, request)
  File "/usr/local/lib/python3.6/site-packages/django/core/handlers/base.py", line 185, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/usr/local/lib/python3.6/site-packages/django/views/decorators/csrf.py", line 58, in wrapped_view
    return view_func(*args, **kwargs)
  File "/usr/local/lib/python3.6/site-packages/django/views/generic/base.py", line 68, in view
    return self.dispatch(request, *args, **kwargs)
  File "/usr/local/lib/python3.6/site-packages/rest_framework/views.py", line 494, in dispatch
    response = self.handle_exception(exc)
  File "/usr/local/lib/python3.6/site-packages/rest_framework/views.py", line 454, in handle_exception
    self.raise_uncaught_exception(exc)
  File "/usr/local/lib/python3.6/site-packages/rest_framework/views.py", line 491, in dispatch
    response = handler(request, *args, **kwargs)
  File "/usr/local/lib/python3.6/site-packages/drf_yasg/views.py", line 88, in get
    schema = generator.get_schema(request, self.public)
  File "/usr/local/lib/python3.6/site-packages/drf_yasg/generators.py", line 203, in get_schema
    paths, prefix = self.get_paths(endpoints, components, request, public)
  File "/usr/local/lib/python3.6/site-packages/drf_yasg/generators.py", line 317, in get_paths
    operations[method.lower()] = self.get_operation(view, path, prefix, method, components, request)
  File "/usr/local/lib/python3.6/site-packages/drf_yasg/generators.py", line 357, in get_operation
    operation = view_inspector.get_operation(operation_keys)
  File "/usr/local/lib/python3.6/site-packages/drf_yasg/inspectors/view.py", line 38, in get_operation
    responses = self.get_responses()
  File "/usr/local/lib/python3.6/site-packages/drf_yasg/inspectors/view.py", line 161, in get_responses
    responses=self.get_response_schemas(response_serializers)
  File "/src/lib/ucee-django-utils/ucee_django_utils/schema.py", line 25, in get_response_schemas
    resp_schemas = super().get_response_schemas(response_serializers)
  File "/usr/local/lib/python3.6/site-packages/drf_yasg/inspectors/view.py", line 224, in get_response_schemas
    if not isinstance(response.schema, openapi.Schema.OR_REF):
  File "/usr/local/lib/python3.6/site-packages/drf_yasg/openapi.py", line 102, in __getattr__
    raise AttributeError("object of class " + type(self).__name__ + " has no attribute " + item)
AttributeError: object of class Response has no attribute schema
[22/Feb/2018 13:45:45] "GET /_internal/auth/schema HTTP/1.0" 500 17194

It works if I set the schema kwarg on the Response (either with a serializer or a manually created Schema instance; setting it to no_body doesn't work). As far as I can tell I should be able to specify a Response without a Schema (I can if I set the value of the responses dict to a string)?

Rendering of nested routers in GUI

I stumbled upon this project when searching for documentation tools with support for nested routers. The example that you supply with the articles/{slug}/image is exactly what we use a lot in one of our API's.

In our case, we have several of these endpoints and in most cases each endpoint accepts 4 or 5 HTTP methods. So in the GUI, this is rendered as a huge list of endpoints, which is hard to work with.

Is there a way to group these endpoints as nested collections? So in your example, the image would get it's own heading, just like articles and snippets, but with a title like articles > images.

I hope my question makes sense. If you need more info please let me know.

OpenAPI 3 support

The latest version of the OpenAPI specification has some interesting new features and could be worth implementing. A great overview of new stuff can be found here: https://blog.readme.io/an-example-filled-guide-to-swagger-3-2/

It would enable support for some features we cannot currently represent:

There are also improvements like support for anyOf and oneOf, more complete security schemes, operation links and other goodies.

On the other hand, there are some considerations:

  1. it is significantly more complex than OpenAPI 2.0
  2. it would require non-trivial changes to both public interfaces and internal code
  3. supporting both 2.0 and 3.0 at the same time is a bit complicated, so 2.0 would probably have to be dropped
  4. many tools do not yet support 3.0 - the spec itself was just released in September 2017

So, as of now, this is mostly a statement of intent and personal observations - I do not have any plans of starting work on this in the foreseeable future, and certainly not until the tooling situation improves.

Parameters should use description instead of title

The drf-yasg generates object definitions, where the parameters use "title" attribute (example shown below). I believe this goes against the swagger v2 specification and it should be called "description" instead.

"Login": {
      "required": [
        "password"
      ],
      "type": "object",
      "properties": {
        "username": {
          "title": "Username",
          "type": "string"
        },
        "email": {
          "title": "Email",
          "type": "string",
          "format": "email"
        },
        "password": {
          "title": "Password",
          "type": "string"
        }
      }
    },

Validation error on serializer used only for responses

Note: This may be what #51 was trying to get at...

Trying to do this, but getting a validation error ("... 'ssv': "Unresolvable JSON pointer: 'definitions/Detail'")

class DetailSerializer(serializers.Serializer):
    detail = serializers.CharField()

class ManufacturerViewSet(viewsets.ModelViewSet):

    serializer_class = ManufacturerSerializer
    model = Manufacturer
    queryset = Manufacturer.objects.all()

    @swagger_auto_schema(responses={404: openapi.Response("Not found or Not accessible", DetailSerializer,
                                                          examples={
                                                              'Not found': DetailSerializer({'detail':'Not found'}).data,
                                                              'Not accessible': DetailSerializer({'detail':'Not accessible'}).data,
                                                          },
                                                          )})
    def retrieve(self, request, *args, **kwargs):
        return super().retrieve(self, request, *args, **kwargs)

However, if I add the serializer to recognized model, it does work, e.g.,

class ManufacturerSerializer(serializers.ModelSerializer):

    status = DetailSerializer(many=False)

    class Meta:
        model = Manufacturer
        fields = '__all__'

Full text of validation error...

{'flex': "'paths':\n"
         "    - '/manufacturers/{id}/':\n"
         "        - 'get':\n"
         "            - 'responses':\n"
         "                - '404':\n"
         "                    - 'referenceObject':\n"
         "                        - 'additionalProperties':\n"
         '                            - "When `additionalProperties` is False, '
         'no unspecified properties are allowed. The following unspecified '
         "properties were found:\\n\\t`{'headers', 'description', 'examples', "
         '\'schema\'}`"\n'
         "                        - 'required':\n"
         "                            - '$ref':\n"
         "                                - 'This value is required'\n"
         "                    - 'responseObject':\n"
         "                        - 'schema':\n"
         "                            - '$ref':\n"
         "                                - 'The $ref `#/definitions/Detail` "
         "was not found in the schema'",
 'ssv': "Unresolvable JSON pointer: 'definitions/Detail'"}

Problem adding Model to definitions

Using a simplified serializer for a list view with a limited set of fields does not get a model added to definitions. We get this error instead.

Resolver error at paths./customers-search/.get.responses.200.schema.items.$ref
Could not resolve reference because of: Could not resolve pointer: /definitions/SimpleCustomerSerializerV1 does not exist in document

    @swagger_auto_schema(
        manual_parameters=[search_query_param],
        responses={
            status.HTTP_200_OK: openapi.Response(
                'Customers found.',
                serializers.SimpleCustomerSerializerV1(many=True)),
            status.HTTP_404_NOT_FOUND: 'No matching customers found.',
            status.HTTP_429_TOO_MANY_REQUESTS: 'Rate limit exceeded.',
            status.HTTP_500_INTERNAL_SERVER_ERROR: 'Internal error.',
            status.HTTP_504_GATEWAY_TIMEOUT: 'Request timed out.',
        })
    def list(self, request, *args, **kwargs):

If we remove the custom response for 200, the default behavior works as expected and does generate the model in definitions. The code is very hard to trace to figure out what is going on here.

Field with callable default value generates an invalid schema

Hi @axnsan12, cool project! I tried it out in https://github.com/yunity/karrot-backend and stumbled upon this error:

TypeError: Object of type 'CurrentUserDefault' is not JSON serializable
(full stacktrace)

One of our serializers uses DRFs CurrentUserDefault like this:

class FeedbackSerializer(serializers.ModelSerializer):
    class Meta:
        model = FeedbackModel
        fields = ['given_by']
        extra_kwargs = {'given_by': {'default': serializers.CurrentUserDefault()}}

When enabling validation, flex throws this error:

flex.exceptions.ValidationError: 'definitions':
    - 'Feedback':
        - 'properties':
            - 'properties':
                - 'given_by':
                    - 'default':
                        - "The value of `default` must be of one of the declared types for the schema. `CurrentUserDefault()` is not one of `['string']`"
    - 'User':
        - 'default':
            - "The value of `default` must be of one of the declared types for the schema. `CurrentUserDefault()` is not one of `['object']`"

Is it feasible to add support for CurrentUserDefault into drf-yasg?

Data object for request_body

Is it possible to get rid of "data" node in case when request structure is defined using "request_body" attribute?

screen shot 2018-01-15 at 16 04 04

Add 'tags' in openapi.swagger

Hi,
I want use SwaggerMarkup(https://github.com/Swagger2Markup/swagger2markup-maven-project-template) to convert a Swagger specification into PDF, but it failed when I try to build with the json file which generated via drf-yasg. The failure reason is that the json file has no field 'tags', the specific field list all endpoint names and it is different from a subfield of get/put/patch/delete. It is shown as below.
1

I think adding field ‘tags’ in openapi.swagger to list all endpoint names is clearer to users and the generated Swagger specification after modified will be satisfied with some SwaggerTools. Some modified codes are shown as below.

  1. Define a new Class Tags in openapi.py
  2. Add a new parameter 'tags' in openapi.Schema
    2
  3. Define a new function to get tags in generators.OpenAPISchemaGenerator
  4. Add tags when calling openapi.Swagger in generators.OpenAPISchemaGenerator.get_schema

Second request to docs fails with AttributeError

Steps to reproduce:

  1. Create django project with manage.py startproject, add "drf_yasg" to INSTALLED_APPS.
  2. urls.py:
from django.conf.urls import url
from django.contrib import admin
from drf_yasg import openapi
from drf_yasg.views import get_schema_view
from rest_framework import permissions

urlpatterns = [
    url(r'^admin/', admin.site.urls),
]

schema_view = get_schema_view(
    openapi.Info(
        title="Snippets API",
        default_version='v1'
    ),
    public=True,
    permission_classes=(permissions.AllowAny,)
)

urlpatterns += [
    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'),
]
  1. Start a server with manage.py runserver, first page request correctly shows docs UI, second one fails with AttributeError (trace below)
[15/Dec/2017 11:26:52] "GET /redoc/?format=openapi HTTP/1.1" 200 219
Internal Server Error: /redoc/
Traceback (most recent call last):
  File "/code/mkurnikov/.virtualenvs/project_name/lib/python3.6/site-packages/django/core/handlers/exception.py", line 41, in inner
    response = get_response(request)
  File "/code/mkurnikov/.virtualenvs/project_name/lib/python3.6/site-packages/django/core/handlers/base.py", line 187, in _get_response
    response = self.process_exception_by_middleware(e, request)
  File "/code/mkurnikov/.virtualenvs/project_name/lib/python3.6/site-packages/django/core/handlers/base.py", line 185, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/code/mkurnikov/.virtualenvs/project_name/lib/python3.6/site-packages/drf_yasg/views.py", line 33, in _wrapped_view_func
    response = view_func(request, *args, **kwargs)
  File "/code/mkurnikov/.virtualenvs/project_name/lib/python3.6/site-packages/django/utils/decorators.py", line 141, in _wrapped_view
    result = middleware.process_request(request)
  File "/code/mkurnikov/.virtualenvs/project_name/lib/python3.6/site-packages/django/middleware/cache.py", line 137, in process_request
    response = self.cache.get(cache_key)
  File "/code/mkurnikov/.virtualenv/sproject_name/lib/python3.6/site-packages/django/core/cache/backends/locmem.py", line 54, in get
    return pickle.loads(pickled)
  File "/code/mkurnikov/.virtualenvs/project_name/lib/python3.6/site-packages/drf_yasg/openapi.py", line 190, in __init__
    self.info.version = _version or info._default_version
AttributeError: 'NoneType' object has no attribute '_default_version'

Django 1.11.7, Python 3.6.3, DRF 3.7.3

KeyError: 'title' on schema generation

Hi and thank you for this nice lib!

When I follow the basic startup tuto, the doc pages (swagger, json and redoc) fail with the following error

  File "/home/amoki/.../.v_env/lib/python3.6/site-packages/drf_yasg/inspectors/field.py", line 100, in field_to_swagger_object                                                               
    return make_schema_definition()                
  File "/home/amoki/.../.v_env/lib/python3.6/site-packages/drf_yasg/inspectors/field.py", line 96, in make_schema_definition                                                                 
    del result.title                               
  File "/home/amoki/.../.v_env/lib/python3.6/site-packages/drf_yasg/openapi.py", line 109, in __delattr__                                                                                    
    del self[make_swagger_name(item)]              
KeyError: 'title'     

A simple try/except around the failing line works without problem

    def __delattr__(self, item):
        if item.startswith('_'):
            super(SwaggerDict, self).__delattr__(item)
            return
        try:
            del self[make_swagger_name(item)]
        except:
            for key in self.keys():
                print(key)
            print(self)
            print(item)

And the output is

required
type
properties
Schema([('required', ['checker']), ('type', 'object'), ('properties', OrderedDict([('id', Schema([('title', 'ID'), ('type', 'integer'), ('readOnly', True)])), ('error_detail', Schema([('title', 'Error det
ail'), ('type', 'string')])), ('status', Schema([('title', 'Status'), ('type', 'string'), ('enum', ['C', 'P', 'E'])])), ('result', Schema([('title', 'Result'), ('type', 'string')])), ('collisions', Schema
([('title', 'Collisions'), ('type', 'string')])), ('created_at', Schema([('title', 'Created at'), ('type', 'string'), ('format', 'date-time'), ('readOnly', True)])), ('updated_at', Schema([('title', 'Upda
ted at'), ('type', 'string'), ('format', 'date-time'), ('readOnly', True)])), ('checker', Schema([('title', 'Checker'), ('type', 'integer')]))]))])
title
[django] INFO 2018-03-05 15:48:35,551 basehttp "GET /doc?format=openapi HTTP/1.1" 200 131602

Is there a problem with my view/serializer/model?

Thanks!

Management command?

It would be great to have a management command for generating the Swagger/OpenAPI JSON output so that I could do e.g. ./manage.py generate_swagger swagger.json.

I use the output from drf-yasg as the input to swagger-to-flowtype and this would make that process a little easier.

If you give me a pointer to how I might accomplish it I can take a stab at this myself and send a PR, assuming you're open to having it in drf-yasg. :)

Request parameters example not changing for custom urls in ModelViewset

Hello

Sorry not sure if this is a bug or intended this way!

Basically I have a ModelViewSet which I set schema = AutoSchema()

However when I try add a custom function within the ModelViewSet, it does not update to the correct request input parameters in the docs.

class UploadViewSet(viewsets.ModelViewSet):
    """
    Model ViewSet handling user api calls
    """
    schema = AutoSchema()
    permission_classes = (UploadPermissions,)
    queryset = Upload.objects.all()
    serializer_class = UploadSerializer

    @list_route(
        methods=['POST'],
        schema=ManualSchema(fields=[
            coreapi.Field(
                "conditions",
                required=True,
                location="query",
                schema=coreschema.Object(),
                description='Conditions field',
                type='string',
                example='123'
            ),
            coreapi.Field(
                "expiration",
                required=True,
                location="query",
                schema=coreschema.String(),
                description='Expiration field',
                type='string',
                example='123'
            )
        ]),
        permission_classes=[],
        authentication_classes=[]
    )
    def handle_upload_request(self, request):
          ## Handle request here ###

In the swagger / redoc docs it shows all the urls correctly:

image

The default model viewset models show the request input data as

{started: 'string', name: 'string', type: 'string', priority: integer}

However when I check the request input data for my custom url it is the same as above. Instead I would expect it to be something along the lines of:

{condtions: 'string', expiration: 'string'}

Perhaps I am doing something wrong or this is not supported?

I hope I have explained this problem well. Thanks so much for the help!

Allow declarative schema customization via decorators

Allow declarative customization via decorators for:

  • Path Item - View/ViewSet level
  • Operation object - method level
  • Parameter objects - method level
  • Response objects - method level
  • Schema objects - Serializer level

Urls not correctly "currled"

Hi,

i encountered the following:

when clicking "try it out!" in one of my GET Endpoints the curl prompt beneath it states a wrong statement :curl -X GET "localhost://localhost:8000/api/myendpoint/(P101[0-9]+)" -H "accept: application/json" -H "X-CSRFToken: x4bdC0d9Chl2ZThQEpJCi1WzadO0NTIPVa5FXSqkIRvjJ9xvJvBzvZWZLvQ3viDC"

This is obviously a wrong behavior. The correct, and succesfull, curl should look like this :
curl -X GET "localhost:8000/api/myendpoint/101" -H "accept: application/json" -H "X-CSRFToken: x4bdC0d9Chl2ZThQEpJCi1WzadO0NTIPVa5FXSqkIRvjJ9xvJvBzvZWZLvQ3viDC"

my urls.py looks like this :

[...] url(r'^myendpoint/(?P<pk>[0-9]+)', myendpoint.as_view) [...]
This might be an "localhost" problem (I can not validate the issue for the production system, docs aren't online till now) , but still shouldn't ouccur.

Add support for different read/write schemas

A pain point that often exists with automatic swagger specs with DRF is they reuse the same definition for retrieve/list/post endpoints. This leads to painful client side SDKs generated with swagger-codegen as most/all fields on the resulting model will be optional, requiring SDK consumers to litter their code with null checks (or ignore them at their peril).

A motivating example of this problem is the id field that most django models will have. This field is required on reads (the model will always have an id when retrieving/listing) but a read_only field for creates (not required for post requests). Another example would be a field that is derrives from a null=False model field (required at the DB level) but is not required at the API level (perhaps it's generated by the server's business logic).

Can (does) drf-yasg's inspection of serializers find these characteristics and if so, can it be shown in the resulting spec by defining multiple definitions for a serializer and referencing them appropriately in spec paths?

For context, our team generates SDKs for our APIs in many type safe languages and model definitions such as:

MyModel:
    type: object
    required:
        - name
    properties:
        id:
            type: number
        name:
            type: string

are a headache to use because the resulting model will label id as optional, meaning it must be checked before being passed to other SDK methods that expect a value. E.g.

interface MyModel {
    id?: number;
    name: string;
}

// ...

api.updateMyModel({ id: instance.id }) // compilation error because `.id` might be undefined

Identically named serializers seem to confuse drf-yasg

We had two PatientSerializers defined in our project; one of them was used for both views in the Swagger output when in fact each view used a different PatientSerializer; my workaround was to rename one of them (probably best that they are not identically named, but still an issue that might bite someone else)

Schema properties documented as list, but dict can be more suitable

Here, Schema's properties is documented as being a list of child Schemas and SchemaRefs. But using something like properties=[Schema('foo', ...)] results in documentation showing {'0': ...}, as in the property key is an incrementing integer. If you give it a dictionary that goes against the documentation, it works better: properties={'foo': Schema(...)} results in {'foo': ...} in the documentation.

I'm not too knowledgeable on OpenAPI in general but should the documentation show that dict is also acceptable? Or does it work by accident?

Assertion error when trying to visit the page

Hello

Thanks for the great project! I am trying to get it working with my current setup but am running into the following error

AssertionError at /redoc/
#/definitions/Nested already exists

I think the error has got something to do with how I have defined my ViewSets because it goes away when I remove some of my viewsets from the router.register() option in urls.py

I hope I have explained this well enough?

Schema default value not used

Trying to set a default value for a Schema inside of a Parameter. It does not seem to be getting picked up in Swagger when I use the "Try it out" functionality.

My schema looks like this:

    @swagger_auto_schema(
            operation_id="Get a mission plan",
            responses={
                200: 'test'},
            manual_parameters=[
                openapi.Parameter(
                    name='Organization',
                    in_=openapi.IN_HEADER,
                    description="Organization name",
                    required=True,
                    schema=openapi.Schema(
                        type=openapi.TYPE_STRING,
                        default="Organization 1",
                    )
                )
            ]
        )

Perhaps providing a more in depth example in testproj, that uses more of the OpenApi parameters, would be helpful. From what I read here it seems the example field should be used rather than default, but it does not seem to be an available field in the Schema class?

Add missing tests

  • add tests for all Field types
  • cleanup coverage reports to reduce noise (exclude defensive checks, etc)
  • add tests for settings
  • check for other missing tests

"type" mismatch with "enum" contents

I'm attempting to generate a go client based on an openapi spec for the open source netbox API using drf-yasg. Some fields are ending up with a mismatch between their type fields and enum fields in the generated spec.

The effected fields all use the choices keyword param to map non-string values to a list of labels. There are several examples, some with integer as the underlying type and some with boolean:
InterfaceTemplate.form_factor
PowerPort.connection_status
Rack.width

netbox makes use of serializers to have three different projections of each Django model, the standard model, a nested model when it appears in another object and a writable model for PUTs, POSTs and PATCHes. This means that each of the above fields could appears in multiple places in the generated swagger, normally at least the writable and standard models.

For example:
standard rack
writable rack

In practice, the standard model includes a small sub-object with both the integer and string values:

      "width": {
        "value": 19,
        "label": "19 inches"
      }

While the writable model expects only the value for the field. Width becomes a flat integer:

      "width": 19

Would support for this style of usage be possible?

Can't use Serializer as Schema's object property

My APIView returns following object:

out = {
    "foo": FooSerializer(foo, many=True).data,
    "bar": BarSerializer(bar, many=True).data
}

Trying to represent that object by using Serializer references fails with TypeError: Object of type 'SerializerMetaclass' is not JSON serializable:

@swagger_auto_schema(responses={
    200: openapi.Response('OK', openapi.Schema(type=openapi.TYPE_OBJECT, properties={
        'foo': FooSerializer,
        'bar': BarSerializer
    })),
})

Additionally, I don't have these Serializers used anywhere else, so they don't register themselves as definitions, so following fails too:

@swagger_auto_schema(responses={
    200: openapi.Response('OK', openapi.Schema(type=openapi.TYPE_OBJECT, properties={
        'foo': openapi.SchemaRef(openapi.ReferenceResolver('definitions'), 'Foo'),
        'bar': openapi.SchemaRef(openapi.ReferenceResolver('definitions'), 'Bar')
    })),
})

Seems that in such case, defining the whole response structure is the only option, or am I missing some trick here?

Thanks!

Update README and add real docs

  • update README and setup.py descriptions
  • add sphinx documentation
  • upload to readthedocs
  • add docs build target
  • add source code documentation to classes and methods where possible

Hiding APIs that require authentication

I've setup DRF-YASG but it seems not able to hide APIs that require Authentication.

Below is the configuration.

public_apis = [ 
    # sample-end-point
    url(f'{URL_VERSION}/sample-end-point/', include(sample_end_point.urls)),  # requires authentication
]

  schema_view = get_schema_view(
      openapi.Info(
        title="Swagger Doc",
        default_version='v1',
        description="Test description",
        terms_of_service="https://www.google.com/policies/terms/",
        contact=openapi.Contact(email="[email protected]"),
        license=openapi.License(name="BSD License"),
      ),
      validators=['flex', 'ssv'],
      permission_classes=(permissions.AllowAny,), 
      public=False,
      patterns=public_apis,
  )

urlpatterns = public_apis + [

    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'),
]

With the above configuration, the sample-end-point does not appear on the SwaggerUi. It only shows the Authorize Button and text that says No operations defined in spec!. But if I change thepublic=False to public=True setting, the sample-end-point appears. But that is not what I want as this URL should be accessible only after one logs in.

Is there something am not understanding or a setting am missing?

My Swagger settings are as below:

SWAGGER_SETTINGS = {
    'USE_SESSION_AUTH': False,
    'SECURITY_DEFINITIONS': {
        'api_key': {
            'type': 'apiKey',
            'in': 'header',
            'name': 'Authorization'
        }
    },
    'APIS_SORTER': 'alpha',
    'SUPPORTED_SUBMIT_METHODS': ['get', 'post', 'put', 'delete', 'patch'],
    'OPERATIONS_SORTER': 'alpha'
}

I'd posted the issue on Stack Overflow

Write some tests

The test project is very simplistic and there are currently no automated tests.

Should probably fix that.

Calling .get_object() inside .get_serializer_class() raises AssertionError

Tested on the example project "testproj".
Steps:

  1. In view SnippetDetail, commented out serializer_class.
  2. Added a method get_serializer_class, calling self.get_object() inside it
  3. Opened http://localhost:8000/swagger/
  4. AssertionError raised.
    Exception Type: AssertionError at /swagger/
    Exception Value: Expected view SnippetDetail to be called with a URL keyword argument named "pk". Fix your URL conf, or set the .lookup_field attribute on the view correctly.

oauth2 flow

Hi!

Thank you for this amazing package :).

We are implementing an oauth2 flow with Google and after converting token to our application. For that, we are using django-rest-framework-social-oauth2.

This is our SWAGGER_SETTINGS:

SWAGGER_SETTINGS = {
    "USE_SESSION_AUTH": False,
    "SECURITY_DEFINITIONS": {
        "api_key": {
            "type": "apiKey",
            "name": "access_token",
            "in": "header"
        },
        "google_oauth": {
            "type": "oauth2",
            "authorizationUrl": "http://127.0.0.1:8000/auth/login/google-oauth2",
            "flow": "implicit",
            "scopes": {
                "write:admin": "You are God",
                "read:customer": "You are a simple human"
            }
        }
    }
}

So when we click con "Authorize" button we introduce the client_id and the scope. After that, we are redirected to Google for login and then we are back to our swagger page but we are not authenticated. After Google authorization we need to convert token in order to be authenticated with our application but i don't know how to do that.

The flow is
GET /auth/login/google-oauth2/
GET /auth/login/google-oauth2/
GET /auth/complete/google-oauth2/
GET /

Can you please help me?

Thanks in advance :).

Questions re: DRF renderer_classes

We use djangorestframework-camel-case and set our views' renderer_classes to CamelCaseJSONRenderer.

Because of this our Swagger/OpenAPI output differs from the actual output; a field specified as staff_member is represented as staffMember in the view output but incorrectly as staff_member in the Swagger output.

What's the best way to remedy this, and are you interested in remedying it within this project? (If not, I could always post-process the output myself).

ReDoc failed to render this spec

if i add 'USE_SESSION_AUTH': False or 'SHOW_REQUEST_HEADERS': True to SWAGGER_SETTINGS i get page that said

Oops... ReDoc failed to render this spec can't assign to property "_displayType" on false: not an object

just want to ask, is this something that should not appear or its normal?

Output Schemas into definitions

Allow Schemas to be output into the definitions sections of the Swagger object, and generate definition references instead of inline models.

  • Automatically output a single schema definition per serializer class
  • Allow forcing inline output

For GET request, parsed parameters from request_body are presented as response body

View code

from drf_yasg.utils import swagger_auto_schema
from rest_framework import serializers
from rest_framework.views import APIView


class IndexViewSerializer(serializers.Serializer):
    username = serializers.CharField()


class IndexView(APIView):
    @swagger_auto_schema(request_body=IndexViewSerializer)
    def get(self, request):
        pass

swagger spec generated

{
    "swagger":"2.0",
    "info":{
        "title":"Snippets API",
        "version":"v1"
    },
    "host":"localhost:8000",
    "schemes":[
        "http"
    ],
    "basePath":"/",
    "paths":{
        "/index/":{
            "get":{
                "operationId":"index_list",
                "description":"",
                "parameters":[

                ],
                "responses":{
                    "200":{
                        "description":"",
                        "schema":{
                            "type":"array",
                            "items":{
                                "$ref":"#/definitions/IndexView"
                            }
                        }
                    }
                },
                "consumes":[
                    "application/json"
                ],
                "tags":[
                    "index"
                ]
            },
            "parameters":[

            ]
        }
    },
    "definitions":{
        "IndexView":{
            "required":[
                "username"
            ],
            "type":"object",
            "properties":{
                "username":{
                    "type":"string"
                }
            }
        }
    },
    "securityDefinitions":{
        "basic":{
            "type":"basic"
        }
    }
}

As a workaround, I assume, one should specify parameters as openapi.Parameter objects, like

username_param = openapi.Parameter('username', in_=openapi.IN_QUERY, description='Username',
                                   type=openapi.TYPE_STRING)


class IndexView(APIView):
    @swagger_auto_schema(manual_parameters=[username_param])
    def get(self, request):
        pass

which works just fine.

How to set headers on response returning an array

I have a custom pagination class MyPagination subclassed from PageNumberPagination that puts the count and links to the first, prev, next and last pages in headers on the response. The end point returns an array without an envelope object. I would like to include this in the swagger docs. This is as far as I've gotten:

class MyPaginatorInspector(inspectors.PaginatorInspector):
    def get_paginated_response(self, paginator, response_schema):
        assert response_schema.type == openapi.TYPE_ARRAY, "array return expected for paged response"
        paged_schema = None
        if isinstance(paginator, (MyPagination)):
            paged_schema = openapi.Schema(
                type=openapi.TYPE_ARRAY,
                items=response_schema,
                # The properties worked as long as type was openapi.TYPE_OBJECT
                properties=OrderedDict((
                    ('count', openapi.Parameter('number of elements in result',
                                                openapi.IN_HEADER, type=openapi.TYPE_INTEGER)),
                    ('first', openapi.Parameter('url to the first page in the result',
                                                openapi.IN_HEADER, type=openapi.TYPE_STRING)),
                    ('prev', openapi.Parameter('url to the previous page in the result',
                                               openapi.IN_HEADER, type=openapi.TYPE_STRING)),
                    ('next', openapi.Parameter('url to the next page in the result',
                                               openapi.IN_HEADER, type=openapi.TYPE_STRING)),
                    ('last', openapi.Parameter('url to the last page in the result',
                                               openapi.IN_HEADER, type=openapi.TYPE_STRING)),
                ))
            )

        return paged_schema

def get_list_schema_decorator():
    # ... snip
    return swagger_auto_schema(operation_description=operation_description,
                               manual_parameters=manual_parameters,
                               responses=responses,
                               paginator_inspectors=[MyPaginatorInspector]
                               )

but I get the error that "AssertionError at /swagger/: only object Schema can have properties".

Any pointers would be appreciated

ImportError raised when using DRF 3.7.3.

import drf_yasg.generators raises following ImportError when the version of Django Rest Framework is 3.7.3.

/usr/lib/python3.6/site-packages/drf_yasg/generators.py in <module>()
      7 from django.utils.encoding import force_text
      8 from rest_framework import versioning
----> 9 from rest_framework.compat import URLPattern, URLResolver, get_original_route
     10 from rest_framework.schemas.generators import EndpointEnumerator as _EndpointEnumerator
     11 from rest_framework.schemas.generators import SchemaGenerator, endpoint_ordering

ImportError: cannot import name 'URLPattern'

To solve this, you must use DRF>=3.7.4.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.