美文网首页
Django Rest Framework

Django Rest Framework

作者: 李霖弢 | 来源:发表于2022-07-15 16:57 被阅读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 为字典,实例化后亦可通过这两个字段取值。
    此外可支持传入其他配置参数,如:

    • 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.POSTrequest.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所有视图的基类,实现了上述 requestResponse 的封装,增加了异常捕获,并在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 对其进行筛选
    • 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.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装饰器装饰视图方法,以自定义文档参数

    相关文章

      网友评论

          本文标题:Django Rest Framework

          本文链接:https://www.haomeiwen.com/subject/liaxirtx.html