Custom Grouping on OpenAPI endpoints with Django Rest Framework
Asked Answered
T

3

15

I have a Django project and I am using Django REST framework. I am using drf-spectacular for OpenAPI representation, but I think my problem is not tied to this package, it's seems a more generic OpenAPI thing to me (but not 100% sure if I am right to this).

Assume that I have a URL structure like this:

urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/', include([
        path('v1/', include([
            path('auth/', include('rest_framework.urls', namespace='rest_framework')),
            path('jwt-auth/token/obtain', CustomTokenObtainPairView.as_view(), name='token_obtain_pair'),
            path('jwt-auth/token/refresh', CustomTokenRefreshView.as_view(), name='token_refresh'),
            path('home/', include("home.urls"))
        ]))
    ])),

    # OpenAPI endpoints
    path('swagger/', SpectacularSwaggerView.as_view(url_name='schema-swagger-json'), name='schema-swagger-ui'),
    path('swagger.yaml/', SpectacularAPIView.as_view(), name='schema-swagger-yaml'),
    path('swagger.json/', SpectacularJSONAPIView.as_view(), name='schema-swagger-json'),
    path('redoc/', SpectacularRedocView.as_view(url_name='schema-swagger-yaml'), name='schema-redoc'),
]

In the corresponding swagger UI view, I get all endpoints grouped under api endpoint, e.g.: enter image description here

If add more endpoints under v1, all go under the api endpoint.

What I want to achieve is, to have the endpoints in Swagger grouped differently, e.g. by app. So I'd have home, jwt, another_endpoint, instead of just api, so it will be easier to navigate in Swagger (when I add more endpoints, with the current method it's just showing a massive list of URLs, not very user friendly).

I've read that those groups are being extracted from the first path of a URL, in my case this is api, so if I change the URL structure, I could achieve what I need.

But isn't there another way of doing this? I want to keep my URL structure, and customize how I display this with OpenAPI, so in the end I have a swagger that groups the endpoints by app, so it's easier to navigate and find what you are looking for.

Transgression answered 10/7, 2020 at 8:25 Comment(0)
E
33

you are making it harder than it needs to be. In the global settings you can specify a common prefix regex that strips the unwanted parts. that would clean up both operation_id and tags for you. In your case that would probably be:

SPECTACULAR_SETTINGS = {
    'SCHEMA_PATH_PREFIX': r'/api/v[0-9]',
}

that should result in tags: home, jwt-auth, swagger.json, swagger.yaml

the tags on @extend_schema is merely a convenience to deviate from the default where needed. it would be cumbersome to do this for every operation. see the settings for more details:

https://drf-spectacular.readthedocs.io/en/latest/settings.html

for even more elaborate tagging you can always subclass AutoSchema and override get_tags(self) to your liking. cheers!

Epirogeny answered 13/7, 2020 at 19:16 Comment(3)
Can you please provide an example of using get_tags override?Angell
I think already got an answer to that question in the github issue you raised. I won't link it here, as your specific setup is not the recommended way of dealing with those things.Epirogeny
Yes. You are right. That setup was just hist and try kind of thing. So I am not going to use it.Angell
T
13

Turns out that you can control this by changing the tags in a view, as per OpenAPI specification: https://swagger.io/docs/specification/grouping-operations-with-tags/

So, with drf-spectacular, you can use the extend_schema decorator to achieve this, e.g.:

from drf_spectacular.utils import extend_schema

class CustomTokenObtainPairView(TokenObtainPairView):
    """
    Takes a set of user credentials and returns an access and refresh JSON web
    token pair to prove the authentication of those credentials.
    """
    @extend_schema(
        operation_id="jwt_obtain",
        ....
        tags=["aTestTag"]
    )
    def post(self, request, *args, **kwargs):
        # whatever

So you have to use this decorator to extend the schema in each view that you want to put into a custom group.

Transgression answered 10/7, 2020 at 9:57 Comment(0)
L
2

just use this decorator above your view to group your end points:

@extend_schema(
 description='Store end point',
 tags=['Stores']
)
class Store(viewsets.ModelViewSet):
... rest of your code

image example

Lemay answered 29/2 at 14:55 Comment(1)
Welcome to Stack Overflow! Before answering a question, try to look at the existing answers and check if yours is a duplicate.Garold

© 2022 - 2024 — McMap. All rights reserved.