BestDoctor的REST服务的电池。
项目描述
RestDoctor
BestDoctor的REST服务的电池。
RestDoctor的用途是什么
在BestDoctor,我们有一个自己的API指南,其中说明了API应该如何构建。此外,我们使用Django,并且合理地使用Django Rest Framework。它足够灵活,但我们希望在某些地方有更多的控制和遵循自己的规则。
因此,我们编写了自己的DRF扩展,它具有以下特点:
- API版本之间的完全隔离
- 通过
Accept
头进行版本控制 - 声明式配置序列化和权限类,用于
View
和ViewSet
- 优化的模式生成
快速启动
将restdoctor
包添加为依赖项或通过pip安装,并将restdoctor
添加到INSTALLED_APPS
中。
之后,可以使用restdoctor中的ViewSet,将rest_framework
的导入替换为restdoctor.rest_framework
。
以下是基于DRF教程的示例。之前是:
from django.contrib.auth.models import User
from rest_framework import viewsets
from rest_framework import permissions
from tutorial.quickstart.serializers import UserSerializer, UserListSerializer
class UserViewSet(viewsets.ModelViewSet):
"""
API endpoint that allows users to be viewed or edited.
"""
queryset = User.objects.all().order_by('-date_joined')
serializer_class = UserSerializer
permission_classes = [permissions.IsAuthenticated]
def get_serializer_class(self):
if self.action == 'list':
return UserListSerializer
return self.serializer_class
现在是
from django.contrib.auth.models import User
from restdoctor.rest_framework import viewsets
from rest_framework import permissions
from tutorial.quickstart.serializers import UserSerializer, UserListSerializer
class UserViewSet(viewsets.ModelViewSet):
"""
API endpoint that allows users to be viewed or edited.
"""
queryset = User.objects.all().order_by('-date_joined')
serializer_class_map = {
'default': UserSerializer,
'list': {
'response': UserListSerializer,
},
}
permission_classes_map = {
'default': [permissions.IsAuthenticated]
}
进一步配置
为了解析Accept头中的格式,需要将中间件添加到应用程序的配置中
ROOT_URLCONF = ...
MIDDLEWARE = [
...,
'restdoctor.django.middleware.api_selector.ApiSelectorMiddleware',
]
API_PREFIXES = ('/api',)
API_FORMATS = ('full', 'compact')
之后,对于在API_PREFIXES
中指定的前缀,将解析Accept头。在处理请求的View或ViewSet中,request将添加一个api_params
属性。
安装和配置
在Settings中添加设置
ROOT_URLCONF = 'app.urls'
INSTALLED_APPS = [
...,
'rest_framework',
'restdoctor',
]
MIDDLEWARE = [
...,
'restdoctor.django.middleware.api_selector.ApiSelectorMiddleware',
]
API_FALLBACK_VERSION = 'fallback'
API_FALLBACK_FOR_APPLICATION_JSON_ONLY = False
API_DEFAULT_VERSION = 'v1'
API_DEFAULT_FORMAT = 'full'
API_PREFIXES = ('/api',)
API_FORMATS = ('full', 'compact')
API_RESOURCE_DISCRIMINATIVE_PARAM = 'view_type'
API_RESOURCE_DEFAULT = 'common'
API_RESOURCE_SET_PARAM = False
API_RESOURCE_SET_PARAM_FOR_DEFAULT = False
API_V1_URLCONF = 'api.v1_urls'
API_VERSIONS = {
'fallback': ROOT_URLCONF,
'v1': API_V1_URLCONF,
}
在项目中使用
在rest_framework
和restdoctor.rest_framework
之间有选择时,尽量从restdoctor继承。
from restdoctor.rest_framework.serializers import ModelSerializer
from restdoctor.rest_framework.views import GenericAPIView, RetrieveAPIView, ListAPIView
from restdoctor.rest_framework.viewsets import ModelViewSet, ReadOnlyModelViewSet
版本控制
RestDoctor根据Accept
头将调用路由到隔离的UrlConf
。
- 首先,这意味着如果没有正确的
Accept
头,API接口可能无法访问并返回404。 - 其次,应用程序中可能有多个不同的API版本,它们将不会互相“看到”。
头部的通用格式如下
application/vnd.{vendor}.{version}[-{resource}][.{format}][+json]
其中vendor由应用程序级别的API_VENDOR_STRING
参数设置,版本列表及其与UrlConf的对应关系由API_VERSIONS
参数确定。
中间件ApiSelectorMiddleware
负责对传入请求进行路由,需要将其包括在设置中。
ROOT_URLCONF = 'app.urls'
MIDDLEWARE = [
...,
'restdoctor.django.middleware.api_selector.ApiSelectorMiddleware',
]
API_V1_URLCONF = 'api.v1.urls'
API_VENDOR_STRING = 'RestDoctor'
API_FALLBACK_VERSION = 'fallback'
API_DEFAULT_VERSION = 'v1'
API_VERSIONS = {
API_FALLBACK_VERSION: ROOT_URLCONF,
API_DEFAULT_VERSION: API_V1_URLCONF,
}
API_VERSIONS
的路由在Accept以application/vnd.{vendor}
开始时触发,如果没有指定版本,则使用API_DEFAULT_VERSION
。如果Accept不包含正确的vendor字符串,则选择API_FALLBACK_VERSION
。
版本可以按格式{version}-{resource}
指定,此时ResourceViewSet
将使用此信息来选择ViewSet
。
此外,还可以额外指定{format}
以选择响应格式,实际上是在SerializerClassMapApiView
中选择序列化器。
此外,格式也可以有版本。如果{format}
在API_FORMATS
中设置为version:{2,3,5}
,而请求的Accept中只出现版本号version:5
,则从大到小选择序列化器。
在成功确定来自Accept头的API版本和参数后,中间件将选择用于进一步处理请求的具体UrlConf,并将api_params
属性添加到对象request
中。
API响应格式
我们的API指南中定义了响应格式,由RestDoctorRenderer(restdoctor.rest_framework.renderers.RestDoctorRenderer
)负责。它仅在包含api_params
属性的请求中使用,并且通过在基本View和ViewSet混合类NegotiatedMixin
(restdoctor.rest_framework.mixins.NegotiatedMixin
)中指定的content_negotiation_class
来工作。
SerializerClassMapApiView
DRF允许以相当紧凑的方式定义ModelSeraizlier
+ ModelViewSet
,但留下了一些地方的自由度,而不在其他地方提供。
例如,可以在ViewSet类中重写serializer_class
,或者通过ViewSet.get_serializer_class
动态定义它,但不能单独为请求和响应重写序列化器。也就是说,不能为update
操作指定单独的序列化器,同时使用用于retrieve
操作的序列化器来返回更改后的实体。
SerializerClassMapApiView
允许声明式地设置各种 action 的序列化器,分别针对 request 和 response。
ViewSet 的基本 mixin 支持允许在 rest_framework.viewsets
的导入中使用 ReadOnlyModelViewSet
,在 restdoctor.rest_framework.viewsets
中透明地替换。
serializer_class_map
SerializerClassMapApiView
允许为不同的 action 和响应格式分别设置序列化器,针对 request 和 response 处理阶段。
from restdoctor.rest_framework.viewsets import ModelViewSet
from app.api.serializers import (
MyDefaultSerializer, MyCompactSerializer, MyAntoherSerializer,
MyCreateSerializer, MyUpdateSerializer,
)
class MyApiView(SerializerClassMapApiView):
"""Пример работы с serializer_class_map."""
serializer_class_map = {
'default': MyDefaultSerializer,
'default.compact': MyCompactSerializer,
'create': {
'request': MyCreateSerializer,
},
'update': {
'request': MyUpdateSerializer,
'request.version:3': MyVersion3UpdateSerializer,
'request.version:2': MyVersionUpdateSerializer,
},
'list': {
'response.another_format': MyAnotherSerializer,
'meta': MyMetaSerializer,
}
}
在这个例子中,我们为 ViewSet 设置了 MyDefaultSerializer
作为基本序列化器。但针对 create
和 update
action,我们重新定义了处理 request 的序列化器。
此外,我们为 compact
格式定义了序列化器,并为 list
和 update
action 定义了 another_format
、version:2
和 version:3
格式的序列化器。版本格式的工作原理是寻找精确或较小版本的序列化器。另外,还添加了额外的元信息生成。
permission_classes_map
类似于 serializer_class_map
,可以通过定义 permission_classes_map
来声明式地设置不同 action 的不同 permission_classes
集合。
from restdoctor.rest_framework.viewsets import ModelViewSet
from app.api.permissions import PermissionA, PermissionB
class MyViewSet(ModelViewSet):
permission_classes_map = {
'default': [PermissionA],
'retrieve': [PermissionB],
}
关于 action 的注意
在 DRF 中,action 在使用 Router
注册 ViewSet
时出现。此时,list 和 detail 资源使用不同的 action_maps
进行区分。
list_action_map = {'get': 'list', 'post': 'create'}
detail_action_map = {'get': 'retrieve', 'put': 'update'}
Django 路由机制创建了一个处理函数,该函数实例化带有相应参数的 View/ViewSet。同一个 ViewSet
类将在 UrlConf 中出现两次,具有不同的 action_map
。在处理请求时,根据 HTTP 方法确定 action 并调用相应的 ViewSet
实例方法。在处理请求时,ViewSet
始终设置 self.action
。
然而,对于 View
来说并非如此,因此 SerializerClassMapApiView
添加了一个 action
属性,该属性与 serializer_class_map
中的序列化器搜索相关联。
混合和 ModelViewSet
混合为 ModelViewSet
定义了 'list'
、'retrieve'
、'create'
、'update'
、'destroy'
action 的基本操作。
它们与 DRF 版本的主要区别在于,它们使用 SerializerClassMapApiView.get_request_serializer
和 SerializerClassMapApiView.get_response_serializer
而不是 View.get_serializer
。
RetrieveModelMixin
定义了 retrieve
action 的处理器。定义了 get_item
方法。
class RetrieveModelMixin(BaseListModelMixin):
def retrieve(self, request: Request, *args: typing.Any, **kwargs: typing.Any) -> Response:
item = self.get_item(request_serializer)
...
def get_item(self, request_serializer: BaseSerializer) -> typing.Union[typing.Dict, QuerySet]:
return self.get_object()
即可以使用 RetrieveModelMixin
来处理任何字典,而不仅仅是模型,只需重新定义 ViewSet.get_item
即可。
ListModelMixin
定义了 list
action 的处理器。定义了 get_collection
方法。
class ListModelMixin(BaseListModelMixin):
def list(self, request: Request, *args: typing.Any, **kwargs: typing.Any) -> Response:
queryset = self.get_collection()
...
def get_collection(self, request_serializer: BaseSerializer) -> typing.Union[typing.List, QuerySet]:
return self.filter_queryset(self.get_queryset())
即可以使用 ListModelMixin
来处理任何集合,而不仅仅是模型,只需重新定义 ViewSet.get_collection
即可。在此,如果为 list
指定了序列化器,则它将被用于查询参数,这将允许获取这些参数并附加到 filterset 上。
定义了额外的 meta
信息生成。定义了 get_meta_data
方法。
class ListModelMixin(BaseListModelMixin):
def get_meta_data(self) -> typing.Dict[str, typing.Any]:
return {'test': typing.Any}
即可以使用 ListModelMixin
来生成额外的 meta
信息。为了正确工作,需要定义 meta
的序列化器。
serializer_class_map = {
'default': MyDefaultSerializer,
'list': {
'meta': MyMetaSerializer,
}
}
为分页中选择的数据设置了 perform_list
处理器。为了工作,需要重新定义 perform_list
方法。
class ListModelMixin(BaseListModelMixin):
def perform_list(self, data: typing.Union[typing.List, QuerySet]) -> None:
Sender(data)
ListModelViewSet
仅设置了 list
action 的处理器。
ReadOnlyModelViewSet
设置了 list
和 retrieve
action 的处理器。
CreateUpdateReadModelViewSet
设置了 list
、retrieve
、create
和 update
action 的处理器。
ModelViewSet
包含全部 action:list
、retrieve
、create
、update
、destroy
。
PydanticSerializer
要使用基于 pydantic 的序列化器,需要从 PydanticSerializer
继承,在 Meta
中指定 pydantic_model
和 pydantic_use_aliases
(如有必要)。
pydantic_use_aliases
参数允许使用 pydantic 模型的别名 进行序列化。
class PydanticSerializer(PydanticSerializer):
class Meta:
pydantic_model = PydanticModel
pydantic_use_aliases = True
生成方案
支持生成 3.0.2 和 3.1.0 版本的 openapi 方案。默认方案由 API_DEFAULT_OPENAPI_VERSION
参数指定,默认为 3.0.2
。
生成方案的示例(设置版本)
python3 ./manage.py generateschema --urlconf api.v1.urls --generator_class restdoctor.rest_framework.schema.RefsSchemaGenerator > your_app/static/openapi.schema
生成方案示例版本 openapi 3.0.2
python3 ./manage.py generateschema --urlconf api.v1.urls --generator_class restdoctor.rest_framework.schema.RefsSchemaGenerator30 > your_app/static/openapi-30.schema
生成方案示例版本 openapi 3.1.0
python3 ./manage.py generateschema --urlconf api.v1.urls --generator_class restdoctor.rest_framework.schema.RefsSchemaGenerator31 > your_app/static/openapi-31.schema
生成选项
API_STRICT_SCHEMA_VALIDATION
- 字段描述(
help_text
),模型中的verbose_name
为必填项 - 检查字段注释与属性
allow_null
是否一致 - 检查字段注释与属性
many
是否一致
如果任何检查失败,生成方案将因错误而终止。
API_SCHEMA_PRIORITIZE_SERIALIZER_PARAMETERS
启用此选项后,即使它们重复,也会选择序列化器的字段。
API_SCHEMA_FILTER_MAP_PATH
自定义 DjangoFilterBackend
过滤器处理方案的路径,默认为 restdoctor.rest_framework.schema.filters.FILTER_MAP
。
pre-commit
此存储库使用通过 pre-commit 配置的 git 钩,因此如果计划进一步修改存储库,则需要使用以下命令初始化 pre-commit:
make install-hooks
项目详情
下载文件
下载适用于您平台的应用程序文件。如果您不确定选择哪一个,请了解有关 安装包 的更多信息。
源分布
构建分布
restdoctor-0.0.64.tar.gz 的哈希值
算法 | 哈希摘要 | |
---|---|---|
SHA256 | f6b158c619419d47e5f5ec26bbc224e80409650717a17056da06de53c8eb24f5 |
|
MD5 | ba5a558bcdc65eb2f7d4286acc5b1d63 |
|
BLAKE2b-256 | 5b14ac4e2b2d79e06496b942dffa993a21451f8cd02a94ad6d610b8723e21293 |
restdoctor-0.0.64-py3-none-any.whl 的哈希值
算法 | 哈希摘要 | |
---|---|---|
SHA256 | 3fcc8bb847e353cc55018acf6f97831a7ffed1cd55ca490604fabfa81ba2a0be |
|
MD5 | 3016aba45652ccac0d3ab873bc01d8c2 |
|
BLAKE2b-256 | 624ad881ce3c2bbf4c5fe05caa14d2131742a04f77b88c78f01355b312d3a32e |