zoukankan      html  css  js  c++  java
  • DRF ---- ModelSerializer ListSerializer

    模型类序列化器

    如果我们想要使用序列化器对应的是Django的模型类,DRF为我们提供了ModelSerializer模型类序列化器来帮

    助我们快速创建一个Serializer类。

    ModelSerializer类已经帮我们实现了 create 与 update 方法


    比如我们创建一个BookInfoSerializer

    # 注意点: 继承ModelSerializer 定义内部类 Meta 
    class BookInfoSerializer(serializers.ModelSerializer):
        """图书数据序列化器"""
        class Meta: 
            model = BookInfo 
            fields = '__all__' 
    
    • model 指明参照哪个模型类
    • fields 指明为模型类的哪些字段生成

    1.参数

    model 参数

    model 指明参照哪个模型类

    fields 参数

    使用fields来明确字段,__all__表名包含所有字段,也可以写明具体哪些字段,如

    class BookInfoSerializer(serializers.ModelSerializer):
        """图书数据序列化器"""
        class Meta:
            model = BookInfo
            fields = ('id', 'btitle', 'bpub_date')
    

    extra_kwargs 参数

    我们可以使用extra_kwargs参数为ModelSerializer添加或修改原有的选项参数

    • 划分系统字段为三种:

      • 只读(read_only)

      • 只写(write_only)

      • 可读可写(不设置)

    • 字段是否必须: required选填字段 默认False

    extra_kwargs = {
                'username': {  # 系统字段不设置read_only和write_only,默认都参加
                    'min_length': 3,
                    'max_length': 10,
                    'error_messages': {
                        'min_length': '太短',
                        'max_length': '太长'
                    }
                },
                'gender': {
                    'read_only': True,  # 自定义的序列化字段默认就是read_only,且不能修改,可以省略
                },
         		'sex': {  # 像sex有默认值的字段,为选填字段('required': True可以将其变为必填字段)
                    'write_only': True,
                    'required': True
                }
    		}
    

    exclude 参数

    使用exclude可以明确排除掉哪些字段

    class BookInfoSerializer(serializers.ModelSerializer):
        """图书数据序列化器"""
        class Meta:
            model = BookInfo
            # 与fields不共存,exclude排除哪些字段
            exclude = ('image',)
    

    read_only_fields 参数

    指明只读字段

    class BookInfoSerializer(serializers.ModelSerializer):
        """图书数据序列化器"""
        class Meta:
            model = BookInfo
            fields = ('id', 'btitle', 'bpub_date', 'bread', 'bcomment')
            read_only_fields = ('id', 'bread', 'bcomment')
    

    depth 参数

    自动连表深度

      	# 外键字段默认显示的是外键值(int类型),不会自己进行深度查询
        class Meta:
            model = models.Book
            fields = ('name', 'price', 'img', 'author_list', 'publish')
        
            # 自动连表深度
            depth = 1
    

    2 .自定义序列化字段

    第一种方式

    class UserModelSerializer(serializers.ModelSerializer):
    	#  第一种自定义序列化字段:该字段必须在 fields 中设置
        gender = serializers.SerializerMethodField()
        def get_gender(self, obj):
            return obj.get_sex_display()
    
        class Meta:
            model = models.User
            # fields采用 插拔式 设置所有参与序列化与反序列化字段
            fields = ('gender')
    

    第二种方式

    class User(models.Model):
        sex = models.IntegerField(choices=SEX_CHOICES, default=0, verbose_name='性别')
    
        # 第二种自定义序列化字段(插拔式,提倡使用)
        @property
        def gender(self):
            return self.get_sex_display()
    
    
    class Meta:
        model = models.User
        fields = ('gender')
    

    3.连表深度

    外键字段默认显示的是外键值(int类型),不会自己进行深度查询

    方式一

    自动深度查询的是关联表的所有字段,数据量太多
    
      	# 外键字段默认显示的是外键值(int类型),不会自己进行深度查询
        class Meta:
            model = models.Book
            fields = ('name', 'price', 'img', 'author_list', 'publish')
            # 自动连表深度
            depth = 1
    

    方式二

    必须有子序列化类配合,不能反序列化了
    
    # 可以单独作为Publish接口的序列化类,也可以作为Book序列化外键publish 辅助的序列化组件
    class PublishModelSerializer(ModelSerializer):
        class Meta:
            model = models.Publish
            fields = ('name', 'address')
    
    class BookModelSerializer(ModelSerializer):
        # 自定义连表深度 - 子序列化方式 - 该方式不能参与反序列化
        publish = PublishModelSerializer()
    
        class Meta:
            model = models.Book
            fields = ('name', 'price', 'img', 'author_list', 'publish')
            # 自动连表深度 
            # depth = 1
    

    方式三

    名字不能与外键名同名
    
    	@property
        def publish_info(self):  # 单个数据
            return {
                'name': self.publish.name,
                'address': self.publish.address,
            }
    
        @property
        def author_list(self):
            author_list_temp = []  # 存放所有作者格式化成数据的列表
            authors = self.authors.all()  # 所有作者
            for author in authors:  # 遍历处理所有作者
                author_dic = {
                    'name': author.name,
                }
                try:  # 有详情才处理详情信息
                    author_dic['mobile'] = author.detail.mobile
                except:
                    author_dic['mobile'] = '无'
                author_list_temp.append(author_dic)  # 将处理过的数据添加到数据列表中
            return author_list_temp  # 返回处理后的结果
    

    4 .自定义反序列化字段(重点!)

        # 自定义反序列化字段同Serializer类,且规则只能在此声明中设置,或是在钩子中设置
        # 在extra_kwargs中对其设置的无效
        # 注:自定义反序列化字段与系统字段,设置规则一样,所以必须设置 write_only
        
        (重点!!!)
        re_password = serializers.CharField(min_length=3, max_length=16, write_only=True)
    

    5.钩子校验

    使用方法和 serializer一样

    全局钩子

    	def validate(self, attrs):
    		if attrs.get('xxx'):
                raise ValidationError({'book': '该书已存在'})
            return attrs
    

    局部钩子

        def validate_name(self, value):
            if 'xxx' in value:
                raise ValidationError('该g书不能出版')
            return value
    

    6.四大接口(重中之重)

    序列化与反序列化结合

    """
    1) fields中设置所有序列化与反序列化字段
    2) extra_kwargs划分只序列化或只反序列化字段
        write_only:只反序列化
        read_only:只序列化
        自定义字段默认只序列化(read_only)
    3) 设置反序列化所需的 系统、局部钩子、全局钩子 等校验规则
    """
    class V2BookModelSerializer(ModelSerializer):
        class Meta:
            model = models.Book
            fields = ('name', 'price', 'img', 'author_list', 'publish_name', 'publish', 'authors')
            extra_kwargs = {
                'name': {
                    'required': True,
                    'min_length': 1,
                    'error_messages': {
                        'required': '必填项',
                        'min_length': '太短',
                    }
                },
                'publish': {
                    'write_only': True
                },
                'authors': {
                    'write_only': True
                },
                'img': {
                    'read_only': True,
                },
                'author_list': {
                    'read_only': True,
                },
                'publish_name': {
                    'read_only': True,
                }
            }
    
        def validate_name(self, value):
            # 书名不能包含 g 字符
            if 'g' in value.lower():
                raise ValidationError('该g书不能出版')
            return value
    
        def validate(self, attrs):
            publish = attrs.get('publish')
            name = attrs.get('name')
            if models.Book.objects.filter(name=name, publish=publish):
                raise ValidationError({'book': '该书已存在'})
            return attrs
    

    单查群查

    # 单查群查
        def get(self, request, *args, **kwargs):
            pk = kwargs.get('pk')
            if pk:
                book_obj = models.Book.objects.filter(is_delete=False, pk=pk).first()
                book_ser = serializers.BookModelSerializer(book_obj)
            else:
                book_query = models.Book.objects.filter(is_delete=False).all()
                book_ser = serializers.BookModelSerializer(book_query, many=True)
            return APIResponse(results=book_ser.data)
    

    单删群删

        def delete(self, request, *args, **kwargs):
            """
            单删:接口:/books/(pk)/   数据:空
            群删:接口:/books/   数据:[pk1, ..., pkn]
            逻辑:修改is_delete字段,修改成功代表删除成功,修改失败代表删除失败
            """
            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, '数据有误')
    
            if rows:
                return APIResponse(0, '删除成功')
            return APIResponse(1, '删除失败')
    

    单增群增

     def post(self, request, *args, **kwargs):
            """
            单增:接口:/books/   数据:{...}
            群增:接口:/books/   数据:[{...}, ..., {...}]
            逻辑:将数据给系列化类处理,数据的类型关系到 many 属性是否为True
            """
            if isinstance(request.data, dict):
                many = False
            elif isinstance(request.data, list):
                many = True
            else:
                return Response(data={'detail': '数据有误'}, status=400)
    
            book_ser = serializers.BookModelSerializer(data=request.data, many=many)
            book_ser.is_valid(raise_exception=True)
            book_obj_or_list = book_ser.save()
            return APIResponse(results=serializers.BookModelSerializer(book_obj_or_list, many=many).data)
    

    整体 单改群改

     def put(self, request, *args, **kwargs):
            """
            单改:接口:/books/(pk)/   数据:{...}
            群增:接口:/books/   数据:[{pk, ...}, ..., {pk, ...}]
            逻辑:将数据给系列化类处理,数据的类型关系到 many 属性是否为True
            """
            pk = kwargs.get('pk')
            if pk:  # 单改
                try:
                    # 与增的区别在于,需要明确被修改的对象,交给序列化类
                    book_instance = models.Book.objects.get(is_delete=False, pk=pk)
                except:
                    return Response({'detail': 'pk error'}, status=400)
    
                book_ser = serializers.BookModelSerializer(instance=book_instance, data=request.data)
                book_ser.is_valid(raise_exception=True)
                book_obj = book_ser.save()
                return APIResponse(results=serializers.BookModelSerializer(book_obj).data)
            else:  # 群改
                # 分析(重点):
                # 1)数据是列表套字典,每个字典必须带pk,就是指定要修改的对象,如果有一条没带pk,整个数据有误
                # 2)如果pk对应的对象已被删除,或是对应的对象不存在,可以认为整个数据有误(建议),可以认为将这些错误数据抛出即可
                request_data = request.data
                try:
                    pks = []
                    for dic in request_data:
                        pk = dic.pop('pk')  # 解决分析1,没有pk pop方法就会抛异常
                        pks.append(pk)
    
                    book_query = models.Book.objects.filter(is_delete=False, pk__in=pks).all()
                    if len(pks) != len(book_query):
                        raise Exception('pk对应的数据不存在')
                except Exception as e:
                    return Response({'detail': '%s' % e}, status=400)
    
                book_ser = serializers.BookModelSerializer(instance=book_query, data=request_data, many=True)
                book_ser.is_valid(raise_exception=True)
                book_list = book_ser.save()
                return APIResponse(results=serializers.BookModelSerializer(book_list, many=True).data)
    

    局部单改群改

    在整体改的基础上 添加参数 partial=True

    book_ser = serializers.BookModelSerializer(instance=book_instance, data=request.data, partial=True, context={'request': request})
    
    book_ser = serializers.BookModelSerializer(instance=book_query, data=request_data, many=True, partial=True)
    

    ListSerializer分析(重点)

    • modelSerializer : 有 create(单增加) 有 update (单修改)

    • ListSerializer: 有 create(群增加) 没有 update(群修改)

    • ModelSerializer默认配置了ListSerializer辅助类 只要你群增 或 群改 都会走 ListSerializer()

    配置辅助类

    class BookModelSerializer(serializers.ModelSerializer):
      class Meta:
          # ModelSerializer默认配置了ListSerializer辅助类,帮助完成群增群改
          # 使用list_serializer_class = serializers.ListSerializer 可指定ListSerializer
          # 如果只有群增,是不需要自定义配置的,但要完成群改,必须自定义配置
          list_serializer_class = BookListSerializer
    

    自定义的群增群改辅助类

    class BookListSerializer(serializers.ListSerializer):
        # 群增
        # 自定义的群增群改辅助类,没有必要重写create方法 
        def create(self, validated_data):
            return super().create(validated_data)
    
        # 群改
        def update(self, instance_list, validated_data_list):
            return [
                self.child.update(instance_list[index], attrs) for index, attrs in enumerate(validated_data_list)
            ] # self.child  其实就是你当前自定义的serializers 
    
  • 相关阅读:
    130被围绕的区域
    695岛屿的最大面积
    200岛屿数量
    5314跳跃游戏IV
    375猜数字大小II
    464我能赢吗
    486预测赢家
    877石子游戏
    1000合并石头的最低成本
    5329数组大小减半
  • 原文地址:https://www.cnblogs.com/lddragon1/p/12113437.html
Copyright © 2011-2022 走看看