Hi.
We have the following documentation:

as you can see, endpoints are grouped based on "viewsets" (tags named after its url/viewset name).
If we add a very simple endpoint:
# views.py
class StatusView(APIView):
permission_classes = [AllowAny]
# pylint: disable=redefined-builtin
def get(self, request, *args, **kwargs):
return Response({"status": "ok"})
# urls.py
urlpatterns = [
re_path(r"^status/", StatusView.as_view()),
re_path(
r"^docs/$", SchemaView.with_ui("redoc", cache_timeout=0), name="schema-redoc"
),
re_path(
r"^docs/swagger(?P<format>\.json|\.yaml)/$",
SchemaView.without_ui(cache_timeout=0),
name="schema",
),
re_path(
r"^api/v1/",
include(([*quotas_patterns, *users_patterns], "api"), namespace="v1"),
)
]
the grouping gets broken:


It's because after adding this endpoint, the generated OpenAPI spec tags are simply having api and not the previous names.
Even after trying to tweak it by adding swagger_auto_schema with explicit tags:
class StatusView(APIView):
permission_classes = [AllowAny]
@never_cache
@swagger_auto_schema(
operation_id="status",
tags=["api"],
responses={200: StatusSerializer},
)
# pylint: disable=redefined-builtin
def get(self, request, *args, **kwargs):
return Response({"status": "ok"})
the grouping is broken - the status is under api group but that's the same as for any other endpoint. In the swagger.yaml schema, I can see that all of endpoints have simply tags: api instead of e.g. tags: quota as it has before.
Is there a way how to fix this?
I think the way to go would be to subclass SwaggerAutoSchema and to override the get_tags() method to return the tags you want. Note that you don't only have to work with the provided operation_keys, you can also access the currently inspected view as self.view if needed.
Yeah, thanks, this did the trick:
from drf_yasg.inspectors import SwaggerAutoSchema
class SquadSwaggerAutoSchema(SwaggerAutoSchema):
def get_tags(self, operation_keys=None):
tags = super().get_tags(operation_keys)
if "api" in tags and operation_keys:
# NOTE: `operation_keys` is a list like ["api", "v1", "token", "read"].
tags[0] = operation_keys[2]
return tags
cc who came up with it @DusanMadar
In my case I've implemented following solution, in you settings define default schema class:
SWAGGER_SETTINGS = {
'DEFAULT_AUTO_SCHEMA_CLASS': 'app.models.CustomSwaggerAutoSchema',
}
and based on the comments of @hnykda and @etene:
from drf_yasg.inspectors import SwaggerAutoSchema
class CustomSwaggerAutoSchema(SwaggerAutoSchema):
def get_tags(self, operation_keys=None):
operation_keys = operation_keys or self.operation_keys
tags = self.overrides.get('tags')
if not tags:
tags = [operation_keys[0]]
if hasattr(self.view, "swagger_tags"):
tags = self.view.swagger_tags
return tags
Then you can define the tags on view:
class PageView(ApiView):
...
swagger_tags = ["tag1"]
...
class PageView2(ApiView):
...
swagger_tags = ["tag1", "tag2"]
...
class PageView3(ApiView):
...
swagger_tags = ["tag3"]
...
Most helpful comment
Yeah, thanks, this did the trick:
cc who came up with it @DusanMadar