Drf-yasg doesn't include the "api/" portion of the urls

I'm using drf-yasg to generate a Swagger schema, but it removes the "api/" portion of the url.

schema_view = get_schema_view(
openapi.Info(
    title="My API",
    default_version='v1',
    description="...",
    terms_of_service="https://www.google.com/policies/terms/",
    contact=openapi.Contact(email="hello@mycompany.com"),
    license=openapi.License(name="BSD License"),
),
public=True,
permission_classes=[permissions.AllowAny],
)
router = routers.DefaultRouter()
router.register(r'spaces', SpacesViewSet, basename='spaces')

urlpatterns = [

url(r'^swagger(?P<format>\.json|\.yaml)$', schema_view.without_ui(cache_timeout=0), name='schema-json'),
path('swagger/', schema_view.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui'),
url(r'^redoc/$', schema_view.with_ui('redoc', cache_timeout=0), name='schema-redoc'),

path('api/', include(router.urls)),
path('api/search-options', SearchPlacesOptionsView.as_view()),
]

result on /swagger result on /swagger

As you can see for the routes from the drf router, it doesn't include the /api portion of the url. However for the regular api/search-options endpoint it removes the /api portion as well, so I don't think it has something to do with the router.

I had the same question and saw no one answer to this question, so I fixed it and I'm gonna share it with you I guess this issue happened because they had plenty of changes in their package(drf_yasg).

You should override get_paths() method of OpenAPISchemaGenerator and have some changes on it,

original OpenAPISchemaGenerator in drf_yasg class:

class OpenAPISchemaGenerator(object):
    # other methods ...
    def get_paths(self, endpoints, components, request, public):
    """Generate the Swagger Paths for the API from the given endpoints.

    :param dict endpoints: endpoints as returned by get_endpoints
    :param ReferenceResolver components: resolver/container for Swagger References
    :param Request request: the request made against the schema view; can be None
    :param bool public: if True, all endpoints are included regardless of access through `request`
    :returns: the :class:`.Paths` object and the longest common path prefix, as a 2-tuple
    :rtype: tuple[openapi.Paths,str]
    """
    if not endpoints:
        return openapi.Paths(paths={}), ''

    prefix = self.determine_path_prefix(list(endpoints.keys())) or ''
    assert '{' not in prefix, "base path cannot be templated in swagger 2.0"

    paths = OrderedDict()
    for path, (view_cls, methods) in sorted(endpoints.items()):
        operations = {}
        for method, view in methods:
            if not self.should_include_endpoint(path, method, view, public):
                continue

            operation = self.get_operation(view, path, prefix, method, components, request)
            if operation is not None:
                operations[method.lower()] = operation

        if operations:
            # since the common prefix is used as the API basePath, it must be stripped
            # from individual paths when writing them into the swagger document
            path_suffix = path[len(prefix):]
            if not path_suffix.startswith('/'):
                path_suffix = '/' + path_suffix
            paths[path_suffix] = self.get_path_item(path, view_cls, operations)

    return self.get_paths_object(paths), prefix

override OpenAPISchemaGenerator in my application CustomizedOpenAPISchemaGenerator class:

from collections import OrderedDict

from drf_yasg import openapi
from drf_yasg.generators import OpenAPISchemaGenerator


class CustomizedOpenAPISchemaGenerator(OpenAPISchemaGenerator):
    def get_paths(self, endpoints, components, request, public):
        if not endpoints:
            return openapi.Paths(paths={}), ''

        prefix = self.determine_path_prefix(list(endpoints.keys())) or ''
        assert '{' not in prefix, "base path cannot be templated in swagger 2.0"

        paths = OrderedDict()
        for path, (view_cls, methods) in sorted(endpoints.items()):
            operations = {}
            for method, view in methods:
                if not self.should_include_endpoint(path, method, view, public):
                    continue

                operation = self.get_operation(view, path, prefix, method, components, request)
                if operation is not None:
                    operations[method.lower()] = operation

            if operations:
                path_suffix = path[len(prefix):]
                if not path_suffix.startswith('/'):
                    path_suffix = '/' + path_suffix
                paths[path] = self.get_path_item(path, view_cls, operations)
                #  Only override this above line of upper level class paths[path_suffix] = ... to paths[path] = ...

        return self.get_paths_object(paths), prefix

I only replace this:

paths[path_suffix] = self.get_path_item(path, view_cls, operations)

with this:

paths[path] = self.get_path_item(path, view_cls, operations)

After that you should path this new generator class to your swagger config, to do this you have two options:

1: Use django settings file:

SWAGGER_SETTINGS = {
    'DEFAULT_GENERATOR_CLASS': 'path.to.CustomizedOpenAPISchemaGenerator',
}
  1. path generator_class argument in get_schema_view() function:
schema_view = get_schema_view(
    #     your other data
    generator_class=path.to.CustomizedOpenAPISchemaGenerator
)
Back to Top