• DRF 05


    Response封装

    经过前面的学习我们可以发现, 我们每次成功响应了前端的请求, 都要按照固定的格式写一遍Response内部包含的数据,并将其返回给前端, 每返回一次就要完整的写一次, 是不是有点麻烦?

    我们可以通过对Response进行简单封装, 来简化我们的代码

    # 封装前
    return Response({
        'status': 0,
        'msg': 'ok',
        'results': serializer_obj.data
    })
    
    # 封装后
    return APIResponse(results=serializer_obj.data)
    
    # 在app文件下新建一个response.py文件
    from rest_framework.response import Response
    
    
    # 定义APIResponse继承Response
    class APIResponse(Response):
        # 重写__init__方法
        def __init__(self, status=0, msg='ok', results=None, http_status=None,
                     headers=None, exception=False, content_type=None, **kwargs):
            # 将status, msg, results, kwargs放到data当中
            data = {
                'status': status,
                'msg': msg
            }
            if results is not None:
                data['results'] = results
    
            data.update(**kwargs)
    
            # 调用父类Response的__init__方法
            super().__init__(data=data, status=http_status, headers=headers, exception=exception, content_type=content_type)
    
    

    深度查询之depth

    • depth是深度查询的一种实现方式
    • 在序列化类中的配置类中设置depth
    • 会根据对应深度的外键字段的主键值, 获取对应的记录
    class PressModerSerializer(serializers.ModelSerializer):
      
        class Meta:
            model = models.Press
            fields = ['name', 'addr', 'books']
            # 设置查询深度为1
            depth = 1
            
            
    -----------------------------------------------------------------------------------------------------
    
    
    {
        "status": 0,
        "msg": "ok",
        "results": [
            {
                "name": "东方出版社",
                "addr": "上海",
                # 不设置查询深度, 显示的是主键值; 设置查询深度, 显示是主键值对应的记录
                "books": [
                    {
                        "id": 1,
                        "is_delete": false,
                        "created_time": "2019-12-26T18:40:09",
                        "name": "三体",
                        "price": "49.90",
                        # 如果设置depth=2, 那下面id=1的press也会被查出来 
                        "press": 1,
                        "authors": [
                            1
                        ]
                    },
                    {
                        "id": 3,
                        "is_delete": false,
                        "created_time": "2019-12-26T18:42:08",
                        "name": "球状闪电",
                        "price": "36.60",
                        "press": 1,
                        "authors": [
                            1
                        ]
                    }
                ]
            },
            
    

    深度查询之自定义@property方法

    • 在模型类中自定义@property属性方法获取外键字段对应的数据, 也可以实现深度查询
    • 外键字段值只对应一条记录的深度查询: 自定义方法只需要返回一个值
    • 外键字段值对应多条记录的深度查询: 自定义方法需要返回多个值
    # models.py
    class Book(Base):
        name = models.CharField(max_length=64)
        price = models.DecimalField(max_digits=6, decimal_places=2)
        press = models.ForeignKey(to='Press', related_name='books', db_constraint=False, on_delete=models.SET_NULL,
                                  null=True)
        authors = models.ManyToManyField(to='Author', related_name='books', db_constraint=False)
    
        # 外键字段只对应一条数据时
        @property
        def book_press(self):
            # 这里也可以利用序列化类
            # from serializers import PressModelSerializer
            # return  PressModerSerializer(self.press).data
            
            return {
                'name': self.press.name,
                'addr': self.press.addr
            }
    
        # 外键字段对应多条数据时
        @property
        def book_authors(self):
            authors = []
            for author in self.authors.all():
    
                author_dic = {
                    'name': author.name,
                }
    
                # 如果作者没有详情, 进行异常捕获
                try:
                    author_dic['mobile'] = author.detail.moblie
                except:
                    author_dic['mobile'] = '无'
    
                authors.append(author_dic)
    
            return authors
            
        
    -----------------------------------------------------------------------------------------------------
    
    
    # serializers.py
    class BookMethodSerializer(serializers.ModelSerializer):
        class Meta:
            model = models.Book
            # 将自定义的@property属性方法在fields中配置
            fields = ['name', 'price', 'book_press', 'book_authors']
    
    

    delete请求实现单删群删

    • 将单删变为群删一条数据: pks = [pk, ]
    • 对涉及数据库内部操作的代码进行异常处理
     # 单删群删
        def delete(self, request, *args, **kwargs):
            """
            单删
                接口: /book/(pk)/, 数据: 空
            群删
                接口: /book/, 数据: [pk1, pk2, ...]
            """
            pk = kwargs.get('pk')
    
            if pk:
                # 将单删变为群删一条数据
                pks = [pk, ]
            else:
                pks = request.data
    
            # 数据有误, 数据库执行会报错
            try:
                rows = models.Book.objects.filter(is_delete=False, pk__in=pks).update(is_delete=True)
            except:
                return APIResponse(1, 'invalid data', http_status=400)
    
            if rows:
                return APIResponse(0, 'delete ok')
            else:
                return APIResponse(1, 'delete fail')
    

    post请求实现单增群增

    • 单增群增的接口都是 /book/
    • 单增请求携带数据的格式是字典, 群增请求携带数据的格式是列表套字典
    • 通过判断请求携带数据的数据类型, 来确定many=True or False
    # 单增群增
        def post(self, request, *args, **kwargs):
            """
            单增
                接口: /book/, 数据: {...}
            群增
                接口: /book/, 数据: [{},{}, ...]
            """
            if isinstance(request.data, dict):
                is_many = False
            elif isinstance(request.data, list):
                is_many = True
            else:
                return APIResponse(0, 'invalid data', http_status=400)
    
            serializer_obj = serializers.BookModelSerializer(data=request.data, many=is_many)
            serializer_obj.is_valid(raise_exception=True)
            book_obj_or_list = serializer_obj.save()
    
            return APIResponse(results=serializers.BookModelSerializer(book_obj_or_list, many=is_many).data)
    

    ListSerializer

    • ModelSerializer的create的方法只能进行单增操作
    • ModelSerializer默认配置了ListSerializer来辅助其完成群增操作
    • ListSerialzer下面create的方法只是对群增数据进行了遍历, 然后调用ModelSerializer的create方法进行数据的入库
    # ModelSerializer下面的create的方法只能实现单增
    def create(self, validated_data):
        (...)    
    	return instance
    
    # ListSerializer下面的create方法
    def create(self, validated_data):
    	return [
            # 对群增数据进行遍历, 遍历一个, 就调用ModelSerializer的create方法来增加一个
            # self.child就是ModelSerializer对象
    		self.child.create(attrs) for attrs in validated_data
    		]
    
    • 如果只是进行群增操作, 我们是没有必要自定义ListSerializer子类, 重写create方法的 (当然确实可以写, 但没必要)
    • 如果进行群改操作, 就需要我们自定义ListSerializer子类, 重写update方法
    # ListSerializer的update方法
    def update(self, instance, validated_data):
        raise NotImplementedError(
            "Serializers with many=True do not support multiple update by "
            "default, only multiple create. For updates it is unclear how to "
            "deal with insertions and deletions. If you need to support "
            "multiple update, use a `ListSerializer` class and override "
            "`.update()` so you can specify the behavior exactly."
        )
    

    put请求实现整体单改和整体群改

    • 群改请求携带数据的数据格式是列表套字典, 且每个字典都必需包含pk
    • 如果有一个字典没有包含pk, 或者pk没有对应的数据, 就直接整体报错
    • 需要借助自定义的ListSerializer类, 重新update方法来实现群改操作
    # views.py
    # 整体单改群改 
    def put(self, request, *args, **kwargs):
        """
        单改
            接口: /book/(pk)/, 数据: {...}
        群改
            接口: /book/, 数据: [{'pk':1,..},{'pk':2,..}, ...]
        """
        pk = kwargs.get('pk')
        # 单改
        if pk:
            try:
                # 注意这里我们使用get方法, 没有的话就报错, 进行异常处理
                book_instance = models.Book.objects.get(is_delete=False, pk=pk)
    
            except:
                return APIResponse(1, 'invalid data', http_status=400)
    
            serializer_obj = serializers.BookModelSerializer(instance=book_instance, data=request.data)
            serializer_obj.is_valid(raise_exception=True)
            book_obj = serializer_obj.save()
    
            return APIResponse(results=serializers.BookModelSerializer(book_obj).data)
    
        # 群改
        else:
            try:
                pks = []
                for dic in request.data:
                    pk = dic.pop('pk')
                    pks.append(pk)
    
                book_query = models.Book.objects.filter(is_delete=False, pk__in=pks).all()
    
                if not len(book_query) == len(book_query):
                    raise Exception('pk error')
    
            except Exception as e:
                return APIResponse(1, msg=f'{e} error', http_status=400)
    
            serializer_obj = serializers.BookModelSerializer(instance=book_query, data=request.data, many=True)
            serializer_obj.is_valid(raise_exception=True)
            book_list = serializer_obj.save()
    
            return APIResponse(results=serializers.BookModelSerializer(book_list, many=True).data)
    
    # serializers.py
    # 自定义ListSerializer子类实现群改操作
    class BookListSerializer(serializers.ListSerializer):
        def update(self, instance_list, validated_data_list):
            return [
                self.child.update(instance_list[index], attrs) for index, attrs in enumerate(validated_data_list)
            ]
    
    
    class BookModelSerializer(serializers.ModelSerializer):
        class Meta:
            # 配置自定义的ListSerializer类
            list_serializer_class = BookListSerializer
    
            model = models.Book
            fields = ['name', 'price', 'book_press', 'book_authors', 'press', 'authors']
            extra_kwargs = {
                'press': {
                    'write_only': True
                },
                'authors': {
                    'write_only': True
                }
            }
    

    patch请求实现局部单改和局部群改

    • 局部改就是在实例化serializer对象的时候加一个partial=True 参数就行
      • 某个字段被提供了值, 则该字段修改
      • 某个字段没有被提供值, 则保留原有的值
    • 实例化serializer对象时设置context参数, 可以将视图类中的数据传递给序列化类下面的钩子函数
    • 局部改是兼容整体改的, 因此我们以后用patch请求进行修改操作就好了
    # views.py
    # 局部单改群改 
    def put(self, request, *args, **kwargs):
        """
        单改
            接口: /book/(pk)/, 数据: {...}
        群改
            接口: /book/, 数据: [{'pk':1,..},{'pk':2,..}, ...]
        """
        pk = kwargs.get('pk')
        # 单改
        if pk:
            try:
                # 注意这里我们使用get方法, 没有的话就报错, 进行异常处理
                book_instance = models.Book.objects.get(is_delete=False, pk=pk)
    
            except:
                return APIResponse(1, 'invalid data', http_status=400)
    		
            # 实例化serializer对象时添加partial=True
            serializer_obj = serializers.BookModelSerializer(instance=book_instance, data=request.data, partial=True)
            serializer_obj.is_valid(raise_exception=True)
            book_obj = serializer_obj.save()
    
            return APIResponse(results=serializers.BookModelSerializer(book_obj).data)
    
        # 群改
        else:
            try:
                pks = []
                for dic in request.data:
                    pk = dic.pop('pk')
                    pks.append(pk)
    
                book_query = models.Book.objects.filter(is_delete=False, pk__in=pks).all()
    
                if not len(book_query) == len(book_query):
                    raise Exception('pk error')
    
            except Exception as e:
                return APIResponse(1, msg=f'{e} error', http_status=400)
            
    		# 实例化serializer对象时添加partial=True
            serializer_obj = serializers.BookModelSerializer(instance=book_query, data=request.data, many=True, partial=True)
            serializer_obj.is_valid(raise_exception=True)
            book_list = serializer_obj.save()
    
            return APIResponse(results=serializers.BookModelSerializer(book_list, many=True).data)
    
  • 相关阅读:
    安装ffmpeg视频软件 Linux
    Linux 计划任务
    分享一个大牛的博客地址
    yii验证码Captcha使用以及为什么验证码不刷新问题
    YII缓存之数据缓存
    YII用户注册和用户登录(二)之登录和注册在视图通过表单使用YII小物件并分析
    YIIMVC之用户注册和用户登录
    yii 总结
    yii linux 自动执行脚本
    yii downlist
  • 原文地址:https://www.cnblogs.com/bigb/p/12111795.html
Copyright © 2020-2023  润新知