Drf-yasg не включает часть "api/" в урлы

Я использую drf-yasg для генерации схемы Swagger, но он удаляет часть "api/" из 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()),
]

результат на /swagger result on /swagger

Как вы можете видеть для маршрутов от drf-маршрутизатора, он не включает /api часть url. Однако для обычной конечной точки api/search-options он также удаляет часть /api, поэтому я не думаю, что это как-то связано с маршрутизатором.

У меня был такой же вопрос, и я не видел ни одного ответа на него, поэтому я исправил его и хочу поделиться с вами Я предполагаю, что эта проблема возникла потому, что у них было много изменений в их пакете (drf_yasg).

Вам следует переопределить метод get_paths() OpenAPISchemaGenerator и внести в него некоторые изменения,

Оригинальный OpenAPISchemaGenerator в классе drf_yasg:

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

переопределить OpenAPISchemaGenerator в моем приложении Класс CustomizedOpenAPISchemaGenerator:

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

Я заменяю только это:

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

с этим:

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

После этого необходимо указать путь к новому классу генератора в конфигурации swagger, для этого у вас есть два варианта:

1: Используйте файл настроек django:

SWAGGER_SETTINGS = {
    'DEFAULT_GENERATOR_CLASS': 'path.to.CustomizedOpenAPISchemaGenerator',
}
  1. аргумент path generator_class в функции get_schema_view():
schema_view = get_schema_view(
    #     your other data
    generator_class=path.to.CustomizedOpenAPISchemaGenerator
)
Вернуться на верх