Django Rest Framework 使用 Swagger 或其他文档自定义 POST URL 具有已定义参数 (request.POST) 的端点

Django Rest Framework custom POST URL endpoints with defined parameter (request.POST) with Swagger or other doc

之前在 Django 1.11 中,我这样定义 Django REST API:

在url.py

url(r'^api/test_token$', api.test_token, name='test_token'),

在api.py

@api_view(['POST'])
def test_token(request):
    # ----- YAML below for Swagger -----
    """
    description: test_token
    parameters:
      - name: token
        type: string
        required: true
        location: form       
    """
    token = request.POST['token']

    return Response("test_token success", status=status.HTTP_200_OK)

现在我正在迁移到 Django 3.1.5,我想知道如何使用 Django Rest Framework (DRF) 以同样的方式实现上述目标。在上面的特殊情况下,它是 POST API "test_token" 接受一个参数。并生成 API 文档,例如 swagger/redoc(可用于测试 API)

一些注意事项:

如何在 Django 3.x 上实现它? (如标题所示:Django Rest Framework custom POST URL endpoints with defined parameter with Swagger or other doc)

更新:

我认为这里有某种形式的解决方案: https://github.com/tfranzel/drf-spectacular/issues/279

因为我有很多使用@api_view 的API,更改文档字符串 装饰器 @extend_schema 可能是最简单的迁移路径。我希望有人可以提供有关 url.py 到使用 @extend_schema 转换的指导。这是为了实现 url 端点和 swagger。谢谢。

这是我与 drf-spectacular 最接近的

@extend_schema( 
 parameters=[OpenApiParameter( 
     name='token', 
     type={'type': 'string'}, 
     location=OpenApiParameter.QUERY, 
     required=False, 
     style='form', 
     explode=False, 
 )], 
 responses=OpenApiTypes.OBJECT, 
) 
@api_view(['POST'])
def test_api(request):
    # ----- YAML below for Swagger -----
    """
    description: test_api
    parameters:
      - name: token
        type: string
        required: true
        location: form       
    """
    token = request.POST['token']
    
    return Response("success test_api:" + token, status=status.HTTP_200_OK)

它给出了这个(这是不正确的),请注意令牌查询

curl -X POST "http://localhost:8000/api/test_token/?token=hello" -H  "accept: application/json" -H  "X-CSRFToken: JyPOSAQx04LK0aM8IUgUmkruALSNwRbeYDzUHBhCjtXafC3tnHRFsxvyg5SgMLhI" -d ""

而不是 POST 输入参数(如何获取?)

curl -X POST --header 'Content-Type: application/x-www-form-urlencoded' --header 'Accept: application/json' --header 'X-CSRFToken: aExHCSwrRyStDiOhkk8Mztfth2sqonhTkUFaJbnXSFKXCynqzDQEzcRCAufYv6MC' -d 'token=hello' 'http://localhost:8000/api/test_token/

解决方案:

url.py

from drf_yasg.utils import swagger_auto_schema 

from rest_framework.response import Response
from rest_framework import status

from rest_framework.decorators import parser_classes
from rest_framework.parsers import FormParser

token = openapi.Parameter('token', openapi.IN_FORM, type=openapi.TYPE_STRING, required=True)
something = openapi.Parameter('something', openapi.IN_FORM, type=openapi.TYPE_INTEGER, required=False)
@swagger_auto_schema(
    method="post",
    manual_parameters=[token, something],
    operation_id="token_api"
)
@api_view(['POST'])
# this is optional and insures that the view gets formdata
@parser_classes([FormParser])
def token_api(request):
    token = request.POST['token']
    something = request.POST['something']
    
    return Response("success test_api:" + token + something, status=status.HTTP_200_OK)


schema_view = get_schema_view(
   openapi.Info(
      title="Snippets API",
      default_version='v1',
      description="Test description",
      terms_of_service="https://www.google.com/policies/terms/",
      contact=openapi.Contact(email="contact@snippets.local"),
      license=openapi.License(name="BSD License"),
   ),
   public=True,
   permission_classes=[permissions.AllowAny],
)


urlpatterns = [
    
    path('token_api', token_api, name='token_api'),

    path('swagger/', schema_view.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui'),
    
] + required_urlpatterns

如您所说,django-rest-swagger 已弃用。

这就是为什么建议使用 drf-yasg

from drf_yasg import openapi
from drf_yasg.utils import swagger_auto_schema    

class ArticleViewSet(viewsets.ModelViewSet):
    @swagger_auto_schema(request_body=openapi.Schema(
        type=openapi.TYPE_OBJECT, 
        properties={
            'test_token': openapi.Schema(type=openapi.TYPE_STRING, description='string'),
        }
    ))
    def create(self, request, *args, **kwargs):
        ...

或者如果您想使用 DRF 操作

    @swagger_auto_schema(method="post", request_body=openapi.Schema(
        type=openapi.TYPE_OBJECT, 
        properties={
            'test_token': openapi.Schema(type=openapi.TYPE_STRING, description='string'),
        }
    ))
    @action(method=["post"], detail=False)
    def my_post_action(self, request, *args, **kwargs):
        ...

或 api 视图:

# here we define that this view accepts a json (or object parameter) that has test_token parameter inside of it
@swagger_auto_schema(method='post', 
    request_body=openapi.Schema(
    type=openapi.TYPE_OBJECT, # object because the data is in json format
    properties={
        'test_token': openapi.Schema(type=openapi.TYPE_STRING, description='this test_token is used for...'),
    }
), operation_id="token_view")
# your view
@api_view(['POST'])
def token_view(request):
    pass

你的 url.py 看起来像这样

# define some basic info about your api for swagger
schema_view = get_schema_view(
   openapi.Info(
      title="Snippets API",
      default_version='v1',
      description="Test description",
      terms_of_service="https://www.google.com/policies/terms/",
      contact=openapi.Contact(email="contact@snippets.local"),
      license=openapi.License(name="BSD License"),
   ),
   public=True,
   permission_classes=[permissions.AllowAny],
)

urlpatterns = [
    # define your api view url
    path('token_view/', token_view),
    # define the url of the swagger ui
    url(r'^swagger/$', schema_view.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui'),
]

如果您只是想要一些东西来测试 API,Django 休息框架实际上带有它自己的可浏览 API。如果您在 APIView 上设置 serializer_class,那么 BrowsableAPIRenderer 将为您计算出所有相关细节。

以下应该可以解决问题:

from rest_framework import serializers, status
from rest_framework.response import Response
from rest_framework.generics import GenericAPIView

class MySerializer(serializers.Serializer):
    token = serializers.CharField()

class MyView(GenericAPIView):

    serializer_class = MySerializer

    def post(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        data = serializer.data

        return Response("test_token success", status=status.HTTP_200_OK)
# urls.py
urlpatterns = [
    ...
    path("api/test_token", views.MyView.as_view(), name="test_token")
]

(请注意,在 Django 2+ 中我们使用 path 而不是旧的 url 模式。如果您仍想使用正则表达式模式,可以使用 path_re)。

以上假定您没有更改渲染器的默认设置。默认值为:

REST_FRAMEWORK = {
    'DEFAULT_RENDERER_CLASSES': [
        'rest_framework.renderers.JSONRenderer',
        'rest_framework.renderers.BrowsableAPIRenderer',
    ]
}

只要浏览到相关端点,您就会有一个不错的测试界面。

在幕后,serializer_class 的设置使它成为可能。这与 DRF 自动生成用于 swagger 或 redoc 之类的模式的方式相同。