You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为Django REST Framework视图集的自定义返回对象构建文档?

Fixing DRF API Docs for Custom Non-Model Responses

Got it, this is a super common pain point when building APIs with DRF—you’ve got a mix of standard model-backed views and custom endpoints that return non-standard JSON, and the auto-documentation tools (which rely on model metadata) don’t pick up the latter. Let’s walk through the best ways to fix this using the two most popular DRF documentation libraries: drf-spectacular (the modern, recommended choice) and drf-yasg (still widely used in older projects).

drf-spectacular is built to handle custom scenarios like this with ease. Here’s the step-by-step:

1. Set Up drf-spectacular First

First, install and configure the package:

pip install drf-spectacular

Add it to your INSTALLED_APPS in settings.py:

INSTALLED_APPS = [
    # ... other apps
    'drf_spectacular',
]

Then set it as your default schema class in DRF settings:

REST_FRAMEWORK = {
    # ... other settings
    'DEFAULT_SCHEMA_CLASS': 'drf_spectacular.openapi.AutoSchema',
}

Don’t forget to add the schema URL to your urls.py:

from drf_spectacular.views import SpectacularAPIView, SpectacularSwaggerView, SpectacularRedocView

urlpatterns = [
    # ... other URLs
    path('api/schema/', SpectacularAPIView.as_view(), name='schema'),
    path('api/docs/', SpectacularSwaggerView.as_view(url_name='schema'), name='swagger-ui'),
    path('api/redoc/', SpectacularRedocView.as_view(url_name='schema'), name='redoc'),
]

2. Define a Serializer for Your Custom Response

Even if your data isn’t tied to a Django model, create a regular Serializer (not ModelSerializer) to define the structure of your custom JSON. This tells the documentation tool exactly what the response will look like:

from rest_framework import serializers

class CustomResponseSerializer(serializers.Serializer):
    status = serializers.CharField(help_text="Status of the request (success/failure)")
    data = serializers.DictField(child=serializers.CharField(), help_text="Custom payload data")
    timestamp = serializers.DateTimeField(help_text="Timestamp of the response")

You can use any DRF serializer fields here—ListField, nested serializers, etc.—to match your actual response structure.

3. Attach the Serializer to Your View/Action

Use the @extend_schema decorator to link your custom serializer to the view or viewset action.

For a Standalone APIView:

from drf_spectacular.utils import extend_schema, OpenApiParameter
from rest_framework.views import APIView
from rest_framework.response import Response
from django.utils import timezone

class CustomDataView(APIView):
    @extend_schema(
        responses=CustomResponseSerializer,
        description="Returns custom non-model data with status and timestamp",
        parameters=[
            OpenApiParameter(name="filter", type=str, description="Filter results by value"),
        ]
    )
    def get(self, request):
        # Your custom logic to generate non-model data
        custom_data = {
            "status": "success",
            "data": {"user_id": request.user.id, "preferences": ["dark_mode", "notifications"]},
            "timestamp": timezone.now()
        }
        return Response(custom_data)

For a ViewSet Custom Action:

If you have a mix of standard ModelViewSet actions and custom ones, just decorate the custom action:

from rest_framework import viewsets
from drf_spectacular.utils import extend_schema
from django.utils import timezone

class MyModelViewSet(viewsets.ModelViewSet):
    queryset = MyModel.objects.all()
    serializer_class = MyModelSerializer

    @extend_schema(
        responses=CustomResponseSerializer,
        description="Custom action that returns aggregated non-model data",
    )
    @action(detail=False, methods=["get"])
    def aggregated_stats(self, request):
        # Custom logic to calculate stats not tied to a single model
        stats = {
            "status": "success",
            "data": {"total_users": User.objects.count(), "active_orders": Order.objects.filter(status="active").count()},
            "timestamp": timezone.now()
        }
        return Response(stats)

Using drf-yasg

If you’re still using drf-yasg, the approach is similar—define a serializer and use a decorator to attach it:

1. Set Up drf-yasg

Install and configure:

pip install drf-yasg

Add to INSTALLED_APPS:

INSTALLED_APPS = [
    # ... other apps
    'drf_yasg',
]

Add the swagger/redoc URLs to urls.py:

from drf_yasg.views import get_schema_view
from drf_yasg import openapi
from rest_framework import permissions

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

urlpatterns = [
    # ... other URLs
    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'),
]

2. Define the Custom Serializer

Same as with drf-spectacular—create a Serializer for your custom response structure.

3. Decorate Your View

Use @swagger_auto_schema to specify the response serializer:

from drf_yasg.utils import swagger_auto_schema
from drf_yasg import openapi
from rest_framework.views import APIView
from rest_framework.response import Response

class CustomDataView(APIView):
    @swagger_auto_schema(
        responses={200: CustomResponseSerializer()},
        operation_description="Returns custom non-model data",
        manual_parameters=[
            openapi.Parameter('filter', openapi.IN_QUERY, description="Filter results", type=openapi.TYPE_STRING)
        ]
    )
    def get(self, request):
        # Custom logic here
        custom_data = {
            "status": "success",
            "data": {"key": "value"},
            "timestamp": timezone.now()
        }
        return Response(custom_data)

Pro Tips

  • Always prefer serializers: Even if you could define the schema manually with OpenAPI types, using serializers keeps your documentation in sync with your actual response validation (if you add it later).
  • Reuse serializers: If multiple views return the same custom structure, reuse the same serializer to keep docs consistent.
  • Add help text: Use the help_text parameter in serializer fields to make your documentation more informative for end-users.

内容的提问来源于stack exchange,提问作者Alex Alifimoff

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 04:24:40