【问题标题】:How to show query parameter options in Django REST Framework - Swagger如何在 Django REST Framework 中显示查询参数选项 - Swagger
【发布时间】:2015-03-27 23:47:45
【问题描述】:

这已经困扰了我一段时间了。

我的最终目标是在 SwaggerUI 中显示查询参数选项,并为每个查询参数提供一个表单输入。类似于为 POST 提供序列化程序时的显示方式。

我正在使用从 GenericViewSet 继承的视图集,我尝试了以下方法:

  • 提供filter_fields属性
  • 提供并将filter_backends 属性设置为(filters.DjangoFilterBackend,)
  • 提供在我的模块中定义的 filter_class。
  • 覆盖options 方法以提供[actions][GET] 信息

这里有个小问题,我没有使用任何模型,所以我认为 DjangoFilterBackend 不会真正帮助我。我正在使用 DjangoRESTFramework 与外部 API 对话,我只是将 JSON 结果返回,并将其传递给前端层。

这里是我的代码的一个小修改 sn-p 以更好地解释我的问题:

views.py

class SomeViewSet(GenericViewSet):
    # Note that I have all of these defined, but I have tried various combinations
    filter_fields = ('query_option_1', 'query_option_2',)
    filter_backeds = (filters.DjangoFilterBackend,)
    filter_class = SomeFilter
    query_metadata = some_dict

    # This works when request is OPTIONS
    def options(self, request, *args, **kwargs):
        if self.metadata_class is None:
            return self.http_method_not_allowed(request, *args, **kwargs)
        data = self.metadata_class().determine_metadata(request, self)
        data['actions']['GET'] = self.query_metadata
        return Response(data, status=status.HTTP_200_OK)

filters.py

class SomeFilter(FilterSet):
    strict = True
    query_option_1 = django_filters.NumberFilter(name='query_option_1')
    query_option_2 = django_filters.NumberFilter(name='query_option_2')

    class Meta:
        fields = ['query_option_1', 'query_option_2']

感谢您的关注,并提前感谢您的回复。

【问题讨论】:

    标签: python django rest django-rest-framework


    【解决方案1】:

    新的招摇

    from rest_framework.filters import BaseFilterBackend
    import coreapi
    
    class SimpleFilterBackend(BaseFilterBackend):
        def get_schema_fields(self, view):
            return [coreapi.Field(
                name='query',
                location='query',
                required=False,
                type='string'
            )]
    
    class MyViewSet(viewsets.ViewSet):
        filter_backends = (SimpleFilterBackend,)
    
        def list(self, request, *args, **kwargs):
            # print(request.GET.get('query'))  # Use the query param in your view
            return Response({'hello': 'world'}, status.HTTP_200_OK)
    

    【讨论】:

    • 在 Django rest swagger 2 中,这是相关的答案,因为不推荐使用文档字符串。
    • 我必须添加一个方法filter_queryset(self, request, queryset, view),它什么都不做,因为BaseFilterBackend 在尝试过滤查询集时会引发NotImplementedError
    • @jarussi 您需要导入 BaseFilterBackend from rest_framework.filters import BaseFilterBackend 应该可以完成工作。您还需要pip install coreapi。谢谢@vadimchin,为我工作。
    • 如何为基于函数的 api 视图添加选项 GET 参数?
    【解决方案2】:

    好的,对于那些偶然发现这个问题的人,我已经想通了。这很愚蠢,我因为不知道而感到有点愚蠢,但在我的辩护中,它没有明确记录。在 DRF 文档或 Django REST Swagger 存储库中找不到该信息。相反,它是在 django-rest-framework-docs 下找到的,这是 Django REST Swagger 的构建基础。

    要指定查询参数以在 SwaggerUI 中显示为表单字段,您只需像这样评论:

    def list(self):
        """
        param1 -- A first parameter
        param2 -- A second parameter
        """ 
        ...
    

    swagger 将解析您的 cmets 并为 param1 和 param2 输入表单。 -- 后面是参数说明。

    【讨论】:

    • 我尝试了您的解决方案,但查询参数既没有显示在默认的 HTML API 文档中,也没有显示在 rest_framework_swagger 中。
    • 注意这适用于 swagger 但不适用于 rest_framework_docs
    • @dnit13 DRF 文档中有降价支持,但尚未发布,人们仍然可以将它与自定义模板标签和模板覆盖一起使用。 github.com/manosim/django-rest-framework-docs/pull/127/…
    • @dnit13 你知道通过 DRF 文档获得此功能的方法吗?
    • 对于 Django rest swagger 2+,使用 vadimchin 答案
    【解决方案3】:

    我找到了rest framework swagger docs。 所以我们可以写参数类型(整数,字符),响应等。

    三个--- 是必要的。

    @api_view(["POST"])
    def foo_view(request):
        """
        Your docs
        ---
        # YAML (must be separated by `---`)
    
        type:
          name:
            required: true
            type: string
          url:
            required: false
            type: url
          created_at:
            required: true
            type: string
            format: date-time
    
        serializer: .serializers.FooSerializer
        omit_serializer: false
    
        parameters_strategy: merge
        omit_parameters:
            - path
        parameters:
            - name: name
              description: Foobar long description goes here
              required: true
              type: string
              paramType: form
            - name: other_foo
              paramType: query
            - name: other_bar
              paramType: query
            - name: avatar
              type: file
    
        responseMessages:
            - code: 401
              message: Not authenticated
        """
    

    我们使用ModelViewSets等mixins类的情况怎么样。 我们是否需要定义 list 函数来添加文档? -- 没有

    我们可以这样做:

    class ArticleViewSet(viewsets.ModelViewSet):
    
        """
        Articles.
        ---
        list:    #<--- here!!
            parameters:
                - name: name
                  description: article title
        get_price:
            omit_serializer: true
    
        """
    
        @list_route(methods=['get'])
        def get_price(self, request):
            pass
    

    【讨论】:

    【解决方案4】:

    使用openapi(而不是coreapi),我发现的“最简单”的方式来自core dev comment

    from rest_framework.schemas.openapi import AutoSchema
    
    
    class CustomSchema(AutoSchema):
        def get_operation(self, path, method):
            op = super().get_operation(path, method)
            op['parameters'].append({
                "name": "foo",
                "in": "query",
                "required": True,
                "description": "What foo does...",
                'schema': {'type': 'string'}
            })
            return op
    
    
    class MyViewSet(ModelViewSet):
        schema = CustomSchema()
    
        def get_queryset(self):
            foo = self.request.query_params.get("foo")
            if foo:
                self.queryset = self.queryset.filter(foo=foo)
            return self.queryset
    

    【讨论】:

    • 这还是“最简单”的方式吗? :)
    【解决方案5】:

    免责声明:我使用的是django_filters,因此结果可能会有所不同。 django_filters 使用 DRF ViewSet 中的参数 filter_fields,这可能与不使用 django_filters 不同。

    我从this thread 中获得灵感,并通过以下方式覆盖了过滤后端中的get_schema_fields() 方法。

    settings.py

    REST_FRAMEWORK = {
        ...
        'DEFAULT_FILTER_BACKENDS': ('location.of.custom_backend.CustomDjangoFilterBackend')
        ...
    }
    

    custom_backend.py

    import coreapi
    import coreschema
    from django_filters.rest_framework import DjangoFilterBackend
    
    
    class CustomDjangoFilterBackend(DjangoFilterBackend):
        """
        Overrides get_schema_fields() to show filter_fields in Swagger.
        """
    
        def get_schema_fields(self, view):
            assert (
                coreapi is not None
            ), "coreapi must be installed to use `get_schema_fields()`"
            assert (
                coreschema is not None
            ), "coreschema must be installed to use `get_schema_fields()`"
    
            # append filter fields to existing fields
            fields = super().get_schema_fields(view)
            if hasattr(view, "filter_fields"):
                fields += view.filter_fields
    
            return [
                coreapi.Field(
                    name=field,
                    location='query',
                    required=False,
                    type='string',
                ) for field in fields
            ]
    

    【讨论】:

    • 我发现了另一个小技巧。如果您希望它反映实际的字段类型,而不是在 Swagger 文档中将其显示为默认的“字符串”,则可以从模型中派生字段的实际类型并使用 type=view.serializer_class.Meta.model._meta.get_field(field).get_internal_type() 将其绑定到 coreapi.Field type
    【解决方案6】:

    如果查询参数用于过滤器后端,添加get_schema_operation_parameters方法是最简单的解决方案:

    class SimpleFilterBackend(BaseFilterBackend):
        def filter_queryset(self, request, queryset, view):
            foo = request.query_params.get("foo")
            if foo:
                queryset = queryset.filter(foo=foo)
            return queryset
    
        def get_schema_operation_parameters(self, view):
            return [{
                "name": "foo",
                "in": "query",
                "required": True,
                "description": "What foo does...",
                "schema": {"type": "string"}
            }]
    
    class MyViewSet(ModelViewSet):
        filter_backends = [SimpleFilterBackend]
    

    【讨论】:

    • 这对我有用,而评分最高的答案和接受的答案却没有。 DRF 3.12.4
    【解决方案7】:

    详细说明来自@vadimchin 的上述答案 - 这是一个工作示例。

    # requirements.txt
    
    djangorestframework==3.9.3
    django-rest-swagger==2.2.0
    django==2.2.1
    coreapi==2.3.3
    

    我在我的应用程序中使用视图集。我必须按照@jarussi 的建议实施filter_queryset(self, request, queryset, view)

    # models.py
    
    from django.db import models 
    
    class Recording(models.Model):
        _id = models.AutoField(primary_key=True)
        name = models.CharField(max_length=511)
    
    # serializers.py
    
    from models import Recording
    from rest_framework import serializers
    
    class RecordingSerializer(serializers.ModelSerializer):
        class Meta:
            model = Recording
            fields = '__all__'
    
    # views.py
    
    from rest_framework import viewsets
    from filters import NameFilterBackend
    from serializers import RecordingSerializer
    
    class RecordingViewSet(viewsets.ModelViewSet):
        serializer_class = RecordingSerializer
        queryset = Recording.objects.all()
        filter_backends = (NameFilterBackend,)
    
    # filters.py 
    
    from rest_framework.filters import BaseFilterBackend
    import coreapi
    
    class NameFilterBackend(BaseFilterBackend):
        def get_schema_fields(self, view):
            return [coreapi.Field(
                name='name',
                location='query',
                required=False,
                type='string',
                description='name of recording'
            )]
    
        def filter_queryset(self, request, queryset, view):
            try:
                n = request.query_params['name']
                queryset = queryset.filter(name=n)
            except KeyError:
                # no query parameters
                pass
            return queryset
    

    【讨论】:

    • 您好,先生,我们可以在详细路线上使用它吗?
    【解决方案8】:

    请参考this github issue 解决问题。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-04-27
      • 1970-01-01
      • 2016-05-04
      • 1970-01-01
      相关资源
      最近更新 更多