From ef9e5f20c854791dbb8e9a966278b66a38357280 Mon Sep 17 00:00:00 2001 From: Carlton Gibson Date: Mon, 4 Sep 2017 14:44:20 +0200 Subject: [PATCH] Add schema decorator for FBVs --- docs/api-guide/views.md | 22 ++++++++++++++++++++++ rest_framework/decorators.py | 10 ++++++++++ tests/test_decorators.py | 17 ++++++++++++++++- 3 files changed, 48 insertions(+), 1 deletion(-) diff --git a/docs/api-guide/views.md b/docs/api-guide/views.md index 4fa36d0fc..c64ecba46 100644 --- a/docs/api-guide/views.md +++ b/docs/api-guide/views.md @@ -184,6 +184,28 @@ The available decorators are: Each of these decorators takes a single argument which must be a list or tuple of classes. + +## View schema decorator + +To override the default schema generation for function based views you may use +the `@schema` decorator. This must come *after* (below) the `@api_view` +decorator. For example: + + from rest_framework.decorators import api_view, schema + from rest_framework.schemas import AutoSchema + + class CustomAutoSchema(AutoSchema): + def get_link(*args): + # override view introspection here... + + @api_view(['GET']) + @schema(CustomAutoSchema()) + def view(request): + return Response({"message": "Hello for today! See you tomorrow!"}) + +This decorator takes a single `AutoSchema` instance, an `AutoSchema` subclass +instance or `ManualSchema` instance as described in the [Schemas documentation][schemas], + [cite]: http://reinout.vanrees.org/weblog/2011/08/24/class-based-views-usage.html [cite2]: http://www.boredomandlaziness.org/2012/05/djangos-cbvs-are-not-mistake-but.html [settings]: settings.md diff --git a/rest_framework/decorators.py b/rest_framework/decorators.py index bf9b32aaa..1297f96b4 100644 --- a/rest_framework/decorators.py +++ b/rest_framework/decorators.py @@ -72,6 +72,9 @@ def api_view(http_method_names=None, exclude_from_schema=False): WrappedAPIView.permission_classes = getattr(func, 'permission_classes', APIView.permission_classes) + WrappedAPIView.schema = getattr(func, 'schema', + APIView.schema) + WrappedAPIView.exclude_from_schema = exclude_from_schema return WrappedAPIView.as_view() return decorator @@ -112,6 +115,13 @@ def permission_classes(permission_classes): return decorator +def schema(view_inspector): + def decorator(func): + func.schema = view_inspector + return func + return decorator + + def detail_route(methods=None, **kwargs): """ Used to mark a method on a ViewSet that should be routed for detail requests. diff --git a/tests/test_decorators.py b/tests/test_decorators.py index b187e5fd6..6331742db 100644 --- a/tests/test_decorators.py +++ b/tests/test_decorators.py @@ -6,12 +6,13 @@ from rest_framework import status from rest_framework.authentication import BasicAuthentication from rest_framework.decorators import ( api_view, authentication_classes, parser_classes, permission_classes, - renderer_classes, throttle_classes + renderer_classes, schema, throttle_classes ) from rest_framework.parsers import JSONParser from rest_framework.permissions import IsAuthenticated from rest_framework.renderers import JSONRenderer from rest_framework.response import Response +from rest_framework.schemas import AutoSchema from rest_framework.test import APIRequestFactory from rest_framework.throttling import UserRateThrottle from rest_framework.views import APIView @@ -151,3 +152,17 @@ class DecoratorTestCase(TestCase): response = view(request) assert response.status_code == status.HTTP_429_TOO_MANY_REQUESTS + + def test_schema(self): + """ + Checks CustomSchema class is set on view + """ + class CustomSchema(AutoSchema): + pass + + @api_view(['GET']) + @schema(CustomSchema()) + def view(request): + return Response({}) + + assert isinstance(view.cls.schema, CustomSchema)