如何在 Django REST Framework 中显示查询参数选项 - Swagger
How to show query parameter options in Django REST Framework - Swagger
这已经困扰我一段时间了。
我的最终目标是在 SwaggerUI 中显示查询参数选项,并为每个查询参数提供一个表单输入。类似于为 POST.
提供序列化程序时的显示方式
我正在使用继承自 GenericViewSet 的视图集,并且尝试了以下操作:
- 提供
filter_fields
属性
- 提供
filter_backends
属性并将其设置为 (filters.DjangoFilterBackend,)
- 提供 filter_class 在我的模块中定义。
- 覆盖
options
方法以提供 [actions][GET]
信息
这里有一个小问题,我没有使用任何模型,所以我认为 DjangoFilterBackend 不会真正帮助我。我正在使用 DjangoRESTFramework 与外部 API 对话,我只是得到 JSON 结果,并将其传递到前端层。
这里是我的代码的一个小修改片段,以更好地解释我的问题:
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']
感谢您的关注,并提前感谢您的回复。
好的,对于那些偶然发现这个问题的人,我已经弄明白了。这很愚蠢,我因为不知道而感到有点愚蠢,但在我的辩护中,它没有被清楚地记录下来。在 DRF 文档或 Django REST Swagger 存储库中找不到该信息。相反,它是在 django-rest-framework-docs 下找到的,这是 Django REST Swagger 的基础。
要将您的查询参数指定为在您的 SwaggerUI 中显示为表单字段,您只需像这样评论:
def list(self):
"""
param1 -- A first parameter
param2 -- A second parameter
"""
...
swagger 将解析您的评论并为 param1 和 param2 放置一个表单输入。下面--
是参数的说明。
我找到了 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
"""
我们使用mixin class 的情况如何,例如ModelViewSets
。
我们是否需要定义 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
新招摇
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_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
]
详细说明@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
请参考 this github issue 解决了问题。
使用 openapi(而不是 coreapi),我发现的 "simplest" 方法来自 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
如果在过滤器后端使用查询参数,添加一个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]
这已经困扰我一段时间了。
我的最终目标是在 SwaggerUI 中显示查询参数选项,并为每个查询参数提供一个表单输入。类似于为 POST.
提供序列化程序时的显示方式我正在使用继承自 GenericViewSet 的视图集,并且尝试了以下操作:
- 提供
filter_fields
属性 - 提供
filter_backends
属性并将其设置为(filters.DjangoFilterBackend,)
- 提供 filter_class 在我的模块中定义。
- 覆盖
options
方法以提供[actions][GET]
信息
这里有一个小问题,我没有使用任何模型,所以我认为 DjangoFilterBackend 不会真正帮助我。我正在使用 DjangoRESTFramework 与外部 API 对话,我只是得到 JSON 结果,并将其传递到前端层。
这里是我的代码的一个小修改片段,以更好地解释我的问题:
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']
感谢您的关注,并提前感谢您的回复。
好的,对于那些偶然发现这个问题的人,我已经弄明白了。这很愚蠢,我因为不知道而感到有点愚蠢,但在我的辩护中,它没有被清楚地记录下来。在 DRF 文档或 Django REST Swagger 存储库中找不到该信息。相反,它是在 django-rest-framework-docs 下找到的,这是 Django REST Swagger 的基础。
要将您的查询参数指定为在您的 SwaggerUI 中显示为表单字段,您只需像这样评论:
def list(self):
"""
param1 -- A first parameter
param2 -- A second parameter
"""
...
swagger 将解析您的评论并为 param1 和 param2 放置一个表单输入。下面--
是参数的说明。
我找到了 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
"""
我们使用mixin class 的情况如何,例如ModelViewSets
。
我们是否需要定义 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
新招摇
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_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
]
详细说明@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
请参考 this github issue 解决了问题。
使用 openapi(而不是 coreapi),我发现的 "simplest" 方法来自 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
如果在过滤器后端使用查询参数,添加一个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]