yyz notes yyz notes
首页
  • RBAC权限设计
  • 架构图标设计
  • 账号体系
  • python基础
  • python高级
  • python模块
  • python设计模式
  • python数据结构与算法
  • django
  • django-DRF
  • flask
  • 直接设计开源pip包
  • 直接设计开源项目
  • python示例题/脚本
  • python面试题
  • golang基础
  • golang高级
  • golang常用组件
  • gin框架
  • es6
  • javascript
  • react
  • vue
  • TypeScript
  • mysql
  • redis
  • minio
  • elasticsearch
  • mongodb
  • 消息队列
  • 自动化测试
  • 操作系统

    • linux
    • windows
  • nginx
  • docker
  • k8s
  • git
  • ldap
  • 学习
  • 面试
  • 心情杂货
  • 实用技巧
  • 友情链接
关于
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

益章

可乐鸡翅
首页
  • RBAC权限设计
  • 架构图标设计
  • 账号体系
  • python基础
  • python高级
  • python模块
  • python设计模式
  • python数据结构与算法
  • django
  • django-DRF
  • flask
  • 直接设计开源pip包
  • 直接设计开源项目
  • python示例题/脚本
  • python面试题
  • golang基础
  • golang高级
  • golang常用组件
  • gin框架
  • es6
  • javascript
  • react
  • vue
  • TypeScript
  • mysql
  • redis
  • minio
  • elasticsearch
  • mongodb
  • 消息队列
  • 自动化测试
  • 操作系统

    • linux
    • windows
  • nginx
  • docker
  • k8s
  • git
  • ldap
  • 学习
  • 面试
  • 心情杂货
  • 实用技巧
  • 友情链接
关于
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • python基础

  • python高级

  • python模块

  • python设计模式

  • python数据结构与算法

  • django

  • django-DRF

    • win环境快速搭建drf项目
    • DRF介绍及快速使用
    • 序列化
    • DRF请求和响应
    • DRF类视图
    • 认证
    • 权限
    • 接口访问控制(限流)
    • 版本
    • 分页
    • 解析器
    • 自定义异常格式
    • 自定义响应格式
      • 1. 自定义响应格式介绍
      • 2. 自定义JSONResponse
        • 2.1 目录结构
        • 2.2 自定义 JSONResponse
        • 2.3 APIView使用自定义响应
      • 3. 自定义ModelViewSet
      • 4. 自定义分页类需要修改
      • 5. 自定义Generics
    • 过滤
    • 搜索
    • 排序
  • flask

  • 自己设计开源pip包

  • 自己设计开源项目

  • python小示例

  • python面试题

  • python
  • django-DRF
YiZhang-You
2023-05-20
目录

自定义响应格式

# 1. 自定义响应格式介绍

之前我们讲了自定义异常,主要目的是为了方便移动端开发使用的,但是我们在给移动端提供正常的响应格式的时候,也要像自定义异常一样,具有一定的格式。目前我们返回的数据格式是这样的:

{
    "next": "http://127.0.0.1:8000/api/v1/games/?cursor=cD0x",
    "previous": null,
    "results": [
        {
            "id": 1,
            "name": "帅高高",
            "status": 0
        }
    ]
}
1
2
3
4
5
6
7
8
9
10
11

或者:

{
    "id": 1,
    "name": "帅高高",
    "status": 0
}
1
2
3
4
5

还是那句话,返回这样格式的数据,对于移动端是不友好的,所以我们需要自定义返回消息,返回的结构如下 :

{
    "code": 200,
    "message": "success",
    "data": [
        {
            "id": 1,
            "name": "帅高高",
            "status": 0
        }
    ],
    "next": "http://127.0.0.1:8000/api/v1/games/?cursor=cD0x",
    "previous": null
}
1
2
3
4
5
6
7
8
9
10
11
12
13

或者:

{
    "code": 200,
    "message": "success",
    "data": {
        "id": 1,
        "name": "帅高高",
        "status": 0
    }
}
1
2
3
4
5
6
7
8
9

# 2. 自定义JSONResponse

# 2.1 目录结构

说明:其实是一样的,我们需要新建一个 custom_json_response.py 文件,来自定义我们的 JSONResponse

...
-app06
    -migrations
        ...
    -apps.py
    -admins.py
    -models.py
    -custom_json_response.py  #新建来自定义我们的 JSONResponse
    -custom_model_view_set.py  #新建,自定义ModelViewSet
    -mypagenumberpaginations.py #编辑,分页功能的修改
    ....
...
1
2
3
4
5
6
7
8
9
10
11
12

# 2.2 自定义 JSONResponse

说明:在我们新建的custom_json_response.py中 自定义我们的 JSONResponse。

from django.utils import six
from rest_framework.response import Response
from rest_framework.serializers import Serializer
 
class JsonResponse(Response):
    """
    An HttpResponse that allows its data to be rendered into
    arbitrary media types.
    """
 
    def __init__(self, data=None, code=None, msg=None,
                 status=None,
                 template_name=None, headers=None,
                 exception=False, content_type=None, **kwargs):
        """
        Alters the init arguments slightly.
        For example, drop 'template_name', and instead use 'data'.
        Setting 'renderer' and 'media_type' will typically be deferred,
        For example being set automatically by the `APIView`.
        """
        super(Response, self).__init__(None, status=status)
 
        if isinstance(data, Serializer):
            msg = (
                'You passed a Serializer instance as data, but '
                'probably meant to pass serialized `.data` or '
                '`.error`. representation.'
            )
            raise AssertionError(msg)
 
        self.data = {"code": code, "message": msg, "data": data}
        self.data.update(kwargs)
        self.template_name = template_name
        self.exception = exception
        self.content_type = content_type
 
        if headers:
            for name, value in six.iteritems(headers):
                self[name] = value
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39

# 2.3 APIView使用自定义响应

说明:其实很简单,APIView中很简单的,只需要把 Response 使用为JsonResponse:

from app06.custom_json_response import JsonResponse  #导入自定义响应格式
 
class UserDetail(APIView):
  
    .....
  
    def get(self,request, *args, **kwargs):
        user = self.get_object(kwargs.get('id'))
        ser = UserSerializer(instance=user,context={"request": request})
        return JsonResponse(ser.data, code=200, msg='ok')   #使用自定义JsonResponse格式
  
     ....
1
2
3
4
5
6
7
8
9
10
11
12

# 3. 自定义ModelViewSet

为啥我们需要自定义ModelViewSet呐。因为我们ModelViewSet 默认不是用我们的 自定义的 JsonResponse,而是采用默认的。响应格式已经高度封装了,所以这边自己写一个就是了。首先我们来看看源码带,是不是已经封装好了:

Ctrl + ModelViewSet => CreateModelMixin
1

来看下:

class CreateModelMixin(object):
    """
    Create a model instance.
    """
    def create(self, request, *args, **kwargs):
        ....
        return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)  #用默认的
1
2
3
4
5
6
7

好咧,那我们自己封装一个,首先创建 custom_model_view_set.py 文件:

from rest_framework import status
from rest_framework import viewsets
from .custom_json_response import JsonResponse
 
 
class CustomModelViewSet(viewsets.ModelViewSet):
 
    def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        self.perform_create(serializer)
        headers = self.get_success_headers(serializer.data)
        return JsonResponse(data=serializer.data, msg="success", code=201, status=status.HTTP_201_CREATED,
                            headers=headers)
 
    def list(self, request, *args, **kwargs):
        queryset = self.filter_queryset(self.get_queryset())
        page = self.paginate_queryset(queryset)
        if page is not None:
            serializer = self.get_serializer(page, many=True)
            return self.get_paginated_response(serializer.data)
 
        serializer = self.get_serializer(queryset, many=True)
        return JsonResponse(data=serializer.data, code=200, msg="success", status=status.HTTP_200_OK)
 
    def retrieve(self, request, *args, **kwargs):
        instance = self.get_object()
        serializer = self.get_serializer(instance)
        return JsonResponse(data=serializer.data, code=200, msg="success", status=status.HTTP_200_OK)
 
    def update(self, request, *args, **kwargs):
        partial = kwargs.pop('partial', False)
        instance = self.get_object()
        serializer = self.get_serializer(instance, data=request.data, partial=partial)
        serializer.is_valid(raise_exception=True)
        self.perform_update(serializer)
 
        if getattr(instance, '_prefetched_objects_cache', None):
            # If 'prefetch_related' has been applied to a queryset, we need to
            # forcibly invalidate the prefetch cache on the instance.
            instance._prefetched_objects_cache = {}
 
        return JsonResponse(data=serializer.data, msg="success", code=200, status=status.HTTP_200_OK)
 
    def destroy(self, request, *args, **kwargs):
        instance = self.get_object()
        self.perform_destroy(instance)
        return JsonResponse(data=[], code=204, msg="delete resource success", status=status.HTTP_204_NO_CONTENT)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48

好啦,那这样,我就使用自己定义的ModelViewSet在视图中继承使用:

from .custom_model_view_set import CustomModelViewSet  #导入自定义的
 
class GameView(CustomModelViewSet):  #继承自定义的
 
    queryset = Game.objects.all()
    serializer_class = GameSerializer
1
2
3
4
5
6

效果图如下:

# 4. 自定义分页类需要修改

我们在分页的时候,没有使用自定义的 JsonRespose,导致如下输出:

那这个不是我们想要的,所以我们需要自定义分页类需要修改,编辑 mypagenumberpaginations.py文件:

from rest_framework.pagination import PageNumberPagination, LimitOffsetPagination, CursorPagination
from .custom_json_response import JsonResponse
from rest_framework import status
 
 
#普通分页
class MyPageNumberPagination(PageNumberPagination):
    page_size = 1
    max_page_size = 1
    page_size_query_param = 'size'
    page_query_param = 'page'
 
    def get_paginated_response(self, data):
        return JsonResponse(data=data, code=200, msg="success", status=status.HTTP_200_OK, next=self.get_next_link(),
                            previous=self.get_previous_link(), count=self.page.paginator.count)
 
#切割分页
class MyPageNumberPagination(LimitOffsetPagination):
    default_limit = 1
    limit_query_param = 'limit'
    offset_query_param = 'offset'
    max_limit = 2
 
    def get_paginated_response(self, data):
        return JsonResponse(data=data, code=200, msg="success", status=status.HTTP_200_OK, next=self.get_next_link(),
                            previous=self.get_previous_link(), count=self.count)
 
#加密分页
class MyPageNumberPagination(CursorPagination):
    cursor_query_param = 'cursor'
    page_size = 1
    ordering = 'id'
    page_size_query_param = 'size'
    max_page_size = 1
 
    def get_paginated_response(self, data):
        return JsonResponse(data=data, code=200, msg="success", status=status.HTTP_200_OK, next=self.get_next_link(),
                            previous=self.get_previous_link())
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

注意:根据需求选择一种分页方式。我们这边选择普通分页来测试:

哈哈,是我们想要的啦。

# 5. 自定义Generics

其实自定义Generics跟我们的 ModelViewSet是一样的效果,这边就废话不多说了,直接上代码吧。原理跟ModelViewSet一样。

from rest_framework import status
from rest_framework import viewsets
from .custom_json_response import JsonResponse
from rest_framework import generics
 
 
class ListCreateAPIView(generics.ListCreateAPIView):
 
    def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        self.perform_create(serializer)
        headers = self.get_success_headers(serializer.data)
        return JsonResponse(data=serializer.data, msg="success", code=201, status=status.HTTP_201_CREATED,
                            headers=headers)
 
    def list(self, request, *args, **kwargs):
        queryset = self.filter_queryset(self.get_queryset())
        page = self.paginate_queryset(queryset)
        if page is not None:
            serializer = self.get_serializer(page, many=True)
            return self.get_paginated_response(serializer.data)
 
        serializer = self.get_serializer(queryset, many=True)
        return JsonResponse(data=serializer.data, code=200, msg="success", status=status.HTTP_200_OK)
 
 
class RetrieveUpdateDestroyAPIView(generics.RetrieveUpdateDestroyAPIView):
 
    def retrieve(self, request, *args, **kwargs):
        instance = self.get_object()
        serializer = self.get_serializer(instance)
        return JsonResponse(data=serializer.data, code=200, msg="success", status=status.HTTP_200_OK)
 
    def update(self, request, *args, **kwargs):
        partial = kwargs.pop('partial', False)
        instance = self.get_object()
        serializer = self.get_serializer(instance, data=request.data, partial=partial)
        serializer.is_valid(raise_exception=True)
        self.perform_update(serializer)
 
        if getattr(instance, '_prefetched_objects_cache', None):
            # If 'prefetch_related' has been applied to a queryset, we need to
            # forcibly invalidate the prefetch cache on the instance.
            instance._prefetched_objects_cache = {}
 
        return JsonResponse(data=serializer.data, msg="success", code=200, status=status.HTTP_200_OK)
 
    def destroy(self, request, *args, **kwargs):
        instance = self.get_object()
        self.perform_destroy(instance)
        return JsonResponse(data=[], code=204, msg="delete resource success", status=status.HTTP_204_NO_CONTENT)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52

后面就在对应的视图中导入并使用。

编辑 (opens new window)
自定义异常格式
过滤

← 自定义异常格式 过滤→

最近更新
01
配置yun源
05-24
02
linux-配置python虚拟环境
05-24
03
linux文件目录管理
05-24
更多文章>
Theme by Vdoing | Copyright © 2023-2023 yizhang | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式