DRF是Django的扩展,用于实现 Restful 提供了序列化器 Serializer 、更多的视图类、Mixin 扩展类,且自带接口测试文档
安装
pip install djangorestframework==3.12.4
配置
# settings.py
INSTALLED_APPS = [
'rest_framework'
]
# 默认值见rest_framework.settings.py
REST_FRAMEWORK = {
# API渲染
# 'DEFAULT_RENDERER_CLASSES': ( # 默认响应渲染类
# 'rest_framework.renderers.JSONRenderer', # json渲染器
# 'rest_framework.renderers.BrowsableAPIRenderer', # 浏览API渲染器
# ),
# 分页
# 'DEFAULT_PAGINATION_CLASS': None,
# 'PAGE_SIZE': None,
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination', # 启用分页
'PAGE_SIZE': 10,
# 过滤
# 'DEFAULT_FILTER_BACKENDS': [],
'DEFAULT_FILTER_BACKENDS': ['django_filters.rest_framework.DjangoFilterBackend'],
# 版本控制
# 'DEFAULT_VERSIONING_CLASS': 'apps.core.middleware.version_control.CustomVersioning',
# 登录
# 'DEFAULT_AUTHENTICATION_CLASSES': [ # 身份认证
# 'rest_framework.authentication.SessionAuthentication',
# 'rest_framework.authentication.BasicAuthentication'
# ],
# 'UNAUTHENTICATED_USER': 'django.contrib.auth.models.AnonymousUser',
'UNAUTHENTICATED_USER': None, # 未认证请求的request.user。默认配置依赖django.contrib.auth,因此要禁用
# 'UNAUTHENTICATED_TOKEN': None,
# 权限
# 'DEFAULT_PERMISSION_CLASSES': [
# 'rest_framework.permissions.AllowAny', # 允许所有用户请求
# ],
# 限流拦截
# 'DEFAULT_THROTTLE_CLASSES': [],
# 'DEFAULT_THROTTLE_RATES': {
# 'user': None,
# 'anon': None,
# },
# 错误处理
# 'EXCEPTION_HANDLER': 'rest_framework.views.exception_handler',
}
路由
DRF 改良了 URLConf 配置的写法,除原本的配置方式外,还支持 routers
方式
直接引入
# urls.py
from rest_framework import routers
import article.views
router = routers.DefaultRouter()
router.register(r'article', article.views.ArticleViewSet)
urlpatterns = [
re_path(r"^api/", include(router.urls)),
]
间接引入
# article/router.py
from rest_framework.routers import DefaultRouter
import article.views
article_router = DefaultRouter()
article_router.register("admin_article", article.views.AdminArticleViewSet, basename="admin_article")
article_router.register("article", article.views.ArticleView, basename="article")
# urls.py
from rest_framework.routers import DefaultRouter
from article.router import article_router
router = DefaultRouter()
router.registry.extend(article_router.registry)
urlpatterns = [
re_path(r"^api/", include(route.urls))
]
Serializer(序列化与反序列化)
序列化:把模型对象(many=True时则为QuerySet)转换成字典
反序列化:对字典进行校验,并转成对象
serializers.Serializer
可用于任意类的序列化(不局限于Django模型类)
构造函数:__init__(self, instance=None, data=empty, **kwargs)
其中 instance 为实例对象,data 为字典,实例化后亦可通过这两个字段取值。
此外可支持传入其他配置参数,如:
-
many=True
则表示传入instace为查询集QuerySet -
partial=True
表示将只传入部分字段用于校验,忽略其他字段的required=True
-
context
用于传入一个字典,供 Serializer 类中方法通过self.context['XXX']
取值
字段类型
BooleanField()
NullBooleanField()
CharField(max_length=None, min_length=None, allow_blank=False, trim_whitespace=True)
EmailField(max_length=None, min_length=None, allow_blank=False)
RegexField(regex, max_length=None, min_length=None, allow_blank=False)
SlugField(maxlength=50, min_length=None, allow_blank=False)
URLField(max_length=200, min_length=None, allow_blank=False)
UUIDField(format=’hex_verbose’) format: 1)
IPAddressField(protocol=’both’, unpack_ipv4=False, **options)
IntegerField(max_value=None, min_value=None)
FloatField(max_value=None, min_value=None)
DecimalField(max_digits, decimal_places, coerce_to_string=None, max_value=None, min_value=None)
DateTimeField(format=api_settings.DATETIME_FORMAT, input_formats=None)
DateField(format=api_settings.DATE_FORMAT, input_formats=None)
TimeField(format=api_settings.TIME_FORMAT, input_formats=None)
DurationField()
ChoiceField(choices)
MultipleChoiceField(choices)
FileField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL)
ImageField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL)
ListField(child=, min_length=None, max_length=None)
DictField(child=)
字段通用属性
read_only 表明该字段仅用于序列化输出,默认False
write_only 表明该字段仅用于反序列化输入,默认False
required 表明该字段在反序列化时必须输入,默认True
default 反序列化时使用的默认值
allow_null 表明该字段是否允许传入None,默认False
validators 该字段使用的验证器
label 用于HTML展示API页面时,显示的字段名称
help_text 用于HTML展示API页面时,显示的字段帮助提示信息
error_messages 包含错误编号与错误信息的字典
内置方法、属性
-
get_字段名(self, attrs)
设置SerializerMethodField
类型字段的序列化逻辑 -
validate(self, attrs)
设置全局反序列化逻辑 -
validate_字段名(self, attrs)
指定单独字段的反序列化逻辑 -
is_valid()
进行反序列化校验,返回布尔值
可设置raise_exception=True
,则校验失败时抛出serializers.ValidationError
,并向前端返回HTTP 400 Bad Request -
validated_data
反序列化结果值,必须在执行is_valid()
校验成功后,再获取该属性。
# serializers.py
from rest_framework import serializers
class ArticleAdminSerializer(serializers.Serializer):
content = serializers.CharField(required=True, trim_whitespace=False, max_length=20, error_messages={"required": "请输入内容", "blank": "内容不能为空", "null": "内容不能为空", "max_length": "太长了,小伙子"})
is_show_home = serializers.ChoiceField(required=False, choices=((0, '否'), (1, '是')), error_messages={"invalid_choice": "选择是否首页展示", "null": "是否首页展示不能为空"})
key = serializers.CharField(required=False, allow_null=True, allow_blank=True)
name_by_method = serializers.SerializerMethodField()
def get_name_by_method(self, attrs):
return attrs.name
def validate_key(self, attrs):
if attrs:
queryset = Article.objects.filter(is_delete=0,
key__iexact=attrs)
if self.instance is not None:
queryset = queryset.exclude(pk=self.instance.id)
if queryset.exists():
raise VException(500, '关键字已存在')
return attrs
# views.py
serializer = ArticleAdminSerializer(instance, data=request.data, partial=True)
serializer.is_valid(raise_exception=True)
form_data = serializer.validated_data
serializers.ModelSerializer
继承自Serializer,允许通过Meta
配置模型类,并自动生成其字段及validators,且包含默认的create()
和update()
实现
通过extra_kwargs
可为字段增加或修改validators
也可通过显式声明覆写model中字段的validators,或新增更多字段。
# serializers.py
from rest_framework import serializers
from article.models import Article
class ArticleSerializer(serializers.ModelSerializer):
class Meta:
model = Article # 对应表
# fields = ('name', 'content') # 仅指定部分字段
# fields = "__all__" # 所有字段
exclude = ('id',) # 仅忽略部分字段
extra_kwargs = {
'name': {'max_length': 3,
'error_messages': {"max_length": "长度太长啦"}
# 'write_only': True,
# 'required': True
},
}
serializers.HyperlinkedModelSerializer
与 ModelSerializer 相比,通过新增的url字段代替主键或外键,方便前端直接通过该 url 访问其对应资源而无需自行拼接路径
请求与响应
DRF的各个视图类均继承自View,重写了as_view方法,并对 request 做了封装
request
DRF做了封装(rest_framework.request.Request),提供了一些额外属性
- data
返回解析之后的请求体数据。类似于Django中标准的request.POST
和request.FILES
属性,但提供如下特性:
包含了解析之后的文件和非文件数据
包含了对POST、PUT、PATCH请求方式解析后的数据
利用了REST framework的parsers解析器,不仅支持表单类型数据,也支持JSON数据 - query_params
即Django标准的request.GET
- user
根据认证策略返回用户信息。默认认证用户为django.contrib.auth.models.User
实例,未认证用户为django.contrib.auth.models.AnonymousUser
实例。
Response
封装了新的Response类,集成了Django的各个返回类型
Response(data, status=None, template_name=None, headers=None, content_type=None)
入参
- data:传入字典等Python内建类型,会自动被处理
- status:默认为200
- template_name:渲染模板名
- content_type:通常无需传入,自动根据前端header的accept生成
返回值属性
- data 传入data的序列化结果
- content 传入data的render结果
- status_code 状态码数字
from rest_framework.response import Response
...
return Response({'data': data}, status=status.HTTP_200_OK)
可以修改默认的响应器类型
# settings.py
REST_FRAMEWORK = {
'DEFAULT_RENDERER_CLASSES': ( # 默认响应渲染类
'rest_framework.renderers.JSONRenderer', # json渲染器
# 'rest_framework.renderers.BrowsableAPIRenderer', # 浏览API渲染器
),
}
视图
-
views.APIView
继承自View,是DRF所有视图的基类,实现了上述request
和Response
的封装,增加了异常捕获,并在dispatch()
前对请求进行身份认证、权限检查、流量控制。 -
viewsets.GenericViewSet
较常使用,封装了对象获取、序列化、分页等功能
应与Mixin类配合使用,并为其提供一些封装好的属性和方法:- queryset:指明使用的数据查询集(如
model.objects.all()
) - serializer_class:指明视图使用的序列化器
- pagination_class:指明视图使用的分页控制器,默认采用全局配置。详见分页
- filter_backends:指明视图所用的过滤控制器,默认采用全局配置。详见过滤
- get_queryset:获取配置的 queryset,Mixin中会使用该方法
- get_serializer:获取并执行配置的 serializer_class,Mixin中会使用该方法
- get_object:获取一个model实例对象,Mixin中会使用该方法
默认使用APIView.check_object_permissions
方法检查当前对象的访问权限 - filter_queryset:传入一个queryset,并基于
filter_backends
对其进行筛选
- queryset:指明使用的数据查询集(如
-
viewsets.ModelViewSet
即 GenericViewSet + 下述五种Mixin
mixins
mixins.CreateModelMixin(成功则返回201)
mixins.RetrieveModelMixin
mixins.UpdateModelMixin
mixins.DestroyModelMixin(成功则返回204)
mixins.ListModelMixin
需配合GenericViewSet
使用,提供create()
, retrieve()
, update()
, partial_update()
, destroy()
, list()
等动作方法
自定义action
- 通过路由配置
# urls.py
urlpatterns = [
path('test/', views.Test.as_view({'get':'details'}))
]
- 通过装饰器配置
其中detail
若为True
表示对应单一资源,则url为xxx/<pk>/detail
形式
# views.py
from rest_framework.decorators import action
...
@action(methods=['get'], detail=False)
def details(self, request, *args, **kwargs):
print(self.action) # details
return Response("hello world")
分页
默认不启用,修改全局配置可统一启用分页功能
# settings.py
REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination', # 使用系统自带的分页
'PAGE_SIZE': 10, # 使用 PageNumberPagination 时必须配置该项
}
视图GenericViewSet子类中也可以单独指定或禁用分页
# views.py
from rest_framework.pagination import PageNumberPagination
class ArticleViewSet(viewsets.GenericViewSet):
pagination_class = PageNumberPagination # 单独指定分页方式
pagination_class = None # 禁用分页
}
启用分页后,视图支持以下方法:
- paginate_queryset:根据配置的 pagination_class,处理传入的 queryset ,返回带有分页信息的 queryset
- get_paginated_response:传入带分页信息的serializer.data,返回带分页信息的Response实例
DRF自带如下几种分页,可以基于其创建子类以实现自定义分页
三种分页模式
- PageNumberPagination 普通分页
根据入参 page 指定页数(从1开始) - LimitOffsetPagination 偏移分页
根据入参,从第 offset 条数据,往后返回 limit 条数据 - CursorPagination 游标分页
可传入 size。每次请求会返回cursor,下次请求传入cursor即可从上一次返回的位置继续向下查找
过滤排序
默认不启用,修改全局配置可统一配置过滤类,或在视图GenericViewSet子类中配置
-
SearchFilter
用于模糊查询search_fields
中字段 -
OrderingFilter
用于对ordering_fields
中字段排序
# settings.py
REST_FRAMEWORK = {
'DEFAULT_FILTER_BACKENDS': [],
}
# views.py
from rest_framework.filters import SearchFilter, OrderingFilter
class ArticleViewSet(viewsets.GenericViewSet):
filter_backends = [SearchFilter, OrderingFilter] # 配置过滤类
search_fields = ["name", "content"] # 配置要过滤的字段
ordering_fields = ["id"] # 配置要排序的字段
}
# 前端请求
/api/article/?search=你好&ordering=-id
-
DjangoFilterBackend
通过第三方包django_filters
引入
通过filterset_fields
指定支持查询的字段(精准查询),或通过filter_class
自定义查询规则
pip install django-filter
# settings.py
INSTALLED_APPS = [
...
'django_filters', # 需要注册应用,
]
REST_FRAMEWORK = {
...
'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.DjangoFilterBackend',)
}
class ArticleViewSet(viewsets.GenericViewSet):
# filter_backends = [DjangoFilterBackend] # 配置过滤类
filterset_fields=['name']
}
# 前端请求
/api/article/?name=你好
认证、权限、频率
DRF视图中自动执行了三大验证:认证--->权限--->频率
可通过配置修改默认的认证,及未认证请求的 request.user
和 request.auth
值
# settings.py
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.BasicAuthentication',
'rest_framework.authentication.SessionAuthentication',
),
# 'UNAUTHENTICATED_USER': 'django.contrib.auth.models.AnonymousUser',
'UNAUTHENTICATED_USER': None, # 未认证请求的request.user。默认配置依赖django.contrib.auth,因此要禁用
'UNAUTHENTICATED_TOKEN': None,
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.AllowAny', # 允许所有用户请求
],
}
频率限制(默认不开启)
可根据认证情况配置:
# settings.py
REST_FRAMEWORK = {
'DEFAULT_THROTTLE_CLASSES': (
'rest_framework.throttling.UserRateThrottle', # 使用 DEFAULT_THROTTLE_RATES['user']
'rest_framework.throttling.AnonRateThrottle', # 使用 DEFAULT_THROTTLE_RATES['anon']
),
'DEFAULT_THROTTLE_RATES': {
'user': '1000/day', # 已登录用户访问频次一天1000次
'anon': '3/m', # 未登录用户访问频次一分钟3次
}
}
也可以根据视图配置:
# views.py
class ContactListView(APIView):
throttle_scope = 'contacts'
...
class ContactDetailView(APIView):
throttle_scope = 'contacts'
...
class UploadView(APIView):
throttle_scope = 'uploads'
...
# settings.py
REST_FRAMEWORK = {
'DEFAULT_THROTTLE_CLASSES': (
'rest_framework.throttling.ScopedRateThrottle',
),
'DEFAULT_THROTTLE_RATES': {
'contacts': '1000/day',
'uploads': '20/day'
}
}
错误处理
DRF默认的异常处理,只处理APIException及其子类的异常,其他返回None。
通常可改用自定义方法,加入log日志,并为非APIException的异常添加处理和返回值。
# settings.py
REST_FRAMEWORK = {
'EXCEPTION_HANDLER': 'rest_framework.views.exception_handler',
}
Swagger配置
参考文档: https://github.com/axnsan12/drf-yasg
pip install drf_yasg
# settings.py
INSTALLED_APPS = [
...
'drf_yasg', # swagger接口生成工具
]
# SWAGGER配置
SWAGGER_SETTINGS = {
# 'PERSIST_AUTH': True,
# 'REFETCH_SCHEMA_WITH_AUTH': False,
# 'REFETCH_SCHEMA_ON_LOGOUT': False,
'USE_SESSION_AUTH': False,
# 'SECURITY_DEFINITIONS': {
# 'Bearer': {
# 'type': 'apiKey',
# 'name': 'Access-Token',
# 'in': 'header'
# },
# }
}
# urls.py
from drf_yasg.views import get_schema_view
from drf_yasg import openapi
from rest_framework.permissions import AllowAny
schema_view = get_schema_view(
openapi.Info(
title="我的Swagger",
default_version='v1.0.0',
description="Test Description",
terms_of_service="https://github.com/TwinkleLee",
contact=openapi.Contact(email="624061283@qq.com"),
license=openapi.License(name="BSD License"),
),
public=True,
permission_classes=(AllowAny,),
authentication_classes=()
)
urlpatterns = [
re_path(r'^swagger(?P<format>\.json|\.yaml)$', schema_view.without_ui(cache_timeout=0), name='schema-json'),
re_path(r'^swagger/$', schema_view.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui'),
re_path(r'^redoc/$', schema_view.with_ui('redoc', cache_timeout=0), name='schema-redoc'),
]
此外也可通过@swagger_auto_schema
装饰器装饰视图方法,以自定义文档参数
网友评论