Django Rest Framework

2022-07-15  本文已影响0人  李霖弢

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 为字典,实例化后亦可通过这两个字段取值。
此外可支持传入其他配置参数,如:

字段类型

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 包含错误编号与错误信息的字典

内置方法、属性
# 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),提供了一些额外属性

Response

封装了新的Response类,集成了Django的各个返回类型
Response(data, status=None, template_name=None, headers=None, content_type=None)

入参
返回值属性
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渲染器
    ),
}
视图
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'}))
]
# 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 # 禁用分页
}

启用分页后,视图支持以下方法:

三种分页模式

过滤排序

默认不启用,修改全局配置可统一配置过滤类,或在视图GenericViewSet子类中配置

# 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
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.userrequest.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装饰器装饰视图方法,以自定义文档参数

上一篇下一篇

猜你喜欢

热点阅读