zoukankan      html  css  js  c++  java
  • 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)
    
  • 相关阅读:
    【crontab】误删crontab及其恢复
    New Concept English there (7)
    New Concept English there (6)
    New Concept English there (5)
    New Concept English there (4)
    New Concept English there (3)
    New Concept English there (2)Typing speed exercise
    New Concept English there (1)Typing speed exercise
    New Concept English Two 34 game over
    New Concept English Two 33 94
  • 原文地址:https://www.cnblogs.com/bigb/p/12111795.html
Copyright © 2011-2022 走看看