• Django Rest Framework -Serializers 序列化组件


    Serializers 序列化组件

    为什么要用序列化组件

    当我们做前后端分离的项目~~我们前后端交互一般都选择JSON数据格式,JSON是一个轻量级的数据交互格式。

    那么我们给前端数据的时候都要转成json格式,那就需要对我们从数据库拿到的数据进行序列化。

    接下来我们看下django序列化和rest_framework序列化的对比~~

    Django的序列化方法

    class BooksView(View):
        def get(self, request):
            book_list = Book.objects.values("id", "title", "chapter", "pub_time", "publisher")
            book_list = list(book_list)
            # 如果我们需要取外键关联的字段信息 需要循环获取外键 再去数据库查然后拼接成我们想要的
            ret = []
            for book in book_list:
                pub_dict = {}
                pub_obj = Publish.objects.filter(pk=book["publisher"]).first()
                pub_dict["id"] = pub_obj.pk
                pub_dict["title"] = pub_obj.title
                book["publisher"] = pub_dict
                ret.append(book)
            ret = json.dumps(book_list, ensure_ascii=False, cls=MyJson)
            return HttpResponse(ret)
    
    
    # json.JSONEncoder.default()
    # 解决json不能序列化时间字段的问题
    class MyJson(json.JSONEncoder):
        def default(self, field):
            if isinstance(field, datetime.datetime):
                return field.strftime('%Y-%m-%d %H:%M:%S')
            elif isinstance(field, datetime.date):
                return field.strftime('%Y-%m-%d')
            else:
                return json.JSONEncoder.default(self, field)
    
    .values 序列化结果
    .values版
    # 第二版 django的序列化
    class BookView(View):
        def get(self, request):
            book_queryset = Book.objects.all()
            data = serializers.serialize("json", book_queryset, ensure_ascii=False)
            return HttpResponse(data)
    django serializers

    DRF序列化的方法

    首先,我们要用DRF的序列化,就要遵循人家框架的一些标准,

      -- Django我们CBV继承类是View,现在DRF我们要用APIView

      -- Django中返回的时候我们用HTTPResponse,JsonResponse,render ,DRF我们用Response

      
      -- 安装  rest_framework      pip install djangorestframework
      -- 注册  rest_framework

    序列化

    from rest_framework import serializers
    
    
    class PublisherSerializers(serializers.Serializer):
        id = serializers.IntegerField()
        title = serializers.CharField(max_length=32)
    
    
    class AuthorSerializers(serializers.Serializer):
        id = serializers.IntegerField()
        name = serializers.CharField(max_length=32)
    
    
    class BookSerializers(serializers.Serializer):
        id = serializers.IntegerField()
        title = serializers.CharField(max_length=32)
        pub_time = serializers.DateField()
        category = serializers.CharField(source='get_category_display')  # source叫资源,后面接的都是ORM操作
        publisher = PublisherSerializers()
        author = AuthorSerializers(many=True)  # 多对多
    serializers.py
    from django.shortcuts import render
    from app01 import models
    from rest_framework.views import APIView
    from .serializers import BookSerializers
    from rest_framework.response import Response
    
    
    class BookView(APIView):
        def get(self, request):
            book_queryset = models.Book.objects.all()
            # 用序列化器进行序列化
            ser_obj = BookSerializers(book_queryset, many=True)  # many=True可以放多个
            return Response(ser_obj.data)
    views.py

    外键关系的序列化

    from rest_framework import serializers
    from .models import Book
    
    
    class PublisherSerializer(serializers.Serializer):
        id = serializers.IntegerField(read_only=True)
        title = serializers.CharField(max_length=32)
    
    
    class UserSerializer(serializers.Serializer):
        id = serializers.IntegerField(read_only=True)
        name = serializers.CharField(max_length=32)
        age = serializers.IntegerField()
    
    
    class BookSerializer(serializers.Serializer):
        id = serializers.IntegerField(read_only=True)
        title = serializers.CharField(max_length=32)
        CHOICES = ((1, "Linux"), (2, "Django"), (3, "Python"))
        chapter = serializers.ChoiceField(choices=CHOICES, source="get_chapter_display", read_only=True)
        pub_time = serializers.DateField()
    
        publisher = PublisherSerializer(read_only=True)
        user = UserSerializer(many=True, read_only=True)
    
    外键关系的序列化
    外键关系的序列化

    反序列化

    当前端给我们发post的请求的时候~前端给我们传过来的数据~我们要进行一些校验然后保存到数据库~

    这些校验以及保存工作,DRF的Serializer也给我们提供了一些方法了~~

    首先~我们要写反序列化用的一些字段~有些字段要跟序列化区分开~~

    Serializer提供了.is_valid()  和.save()方法~~

    class BookSerializers(serializers.Serializer):
        id = serializers.IntegerField(required=False)  # required=False反序时候不进行校验
        title = serializers.CharField(max_length=32)
        pub_time = serializers.DateField()
        category = serializers.CharField(source='get_category_display', read_only=True)  # source叫资源,后面接的都是ORM操作
        post_category = serializers.IntegerField(write_only=True)  # write_only=True 只反序时候用
        publisher = PublisherSerializers(read_only=True)  # read_only=True 这个加上只正序的时候用,反序时候不用
        publisher_id = serializers.IntegerField(write_only=True)
        author = AuthorSerializers(many=True, read_only=True)  # 多对多
        author_list = serializers.ListField(write_only=True)
    
        def create(self, validated_data):
            print(validated_data)
            book_obj = models.Book.objects.create(title=validated_data['title'], pub_time=validated_data['pub_time'],
                                                  category=validated_data['post_category'],
                                                  publisher_id=validated_data['publisher_id'])
            book_obj.author.add(*validated_data['author_list'])
            return book_obj
    反序列化serializer.py
    from django.shortcuts import render
    from app01 import models
    from rest_framework.views import APIView
    from .serializers import BookSerializers
    from rest_framework.response import Response
    
    
    class BookView(APIView):
        def get(self, request):
            book_queryset = models.Book.objects.all()
            # 用序列化器进行序列化
            ser_obj = BookSerializers(book_queryset, many=True)  # many=True可以放多个
            return Response(ser_obj.data)
    
        def post(self, request):
            book_obj = request.data
            ser_obj = BookSerializers(data=book_obj)
            if ser_obj.is_valid():
                ser_obj.save()
                return Response(ser_obj.validated_data)
            return Response(ser_obj.errors)
    
    
    class BookEditView(APIView):
        def get(self, request, id):
            book_obj = models.Book.objects.filter(id=id)
            ser_obj = BookSerializers(book_obj)
            return Response(ser_obj.data)
    反序列化views.py
            -- 反序列化 post请求
                -- 确定新增的数据结构
                -- 序列化器
                    -- 正序和反序列化字段不统一
                    -- required=False 只序列化不走校验
                    -- read_only=True  只序列化用
                    -- write_only=True  只反序列化用
                    -- 重写create方法
                -- 验证通过返回ser_obj.validated_data
                -- 验证不通过返回ser_obj.errors
    反序列化总结

    当前端给我们发送put请求的时候,前端传给我们用户要更新的数据,我们要对数据进行部分验证~~

    class BookSerializers(serializers.Serializer):
        id = serializers.IntegerField(required=False)  # required=False反序时候不进行校验
        title = serializers.CharField(max_length=32)
        pub_time = serializers.DateField()
        category = serializers.CharField(source='get_category_display', read_only=True)  # source叫资源,后面接的都是ORM操作
        post_category = serializers.IntegerField(write_only=True)  # write_only=True 只反序时候用
        publisher = PublisherSerializers(read_only=True)  # read_only=True 这个加上只正序的时候用,反序时候不用
        publisher_id = serializers.IntegerField(write_only=True)
        author = AuthorSerializers(many=True, read_only=True)  # 多对多
        author_list = serializers.ListField(write_only=True)
    
        def create(self, validated_data):
      
            book_obj = models.Book.objects.create(title=validated_data['title'], pub_time=validated_data['pub_time'],
                                                  category=validated_data['post_category'],
                                                  publisher_id=validated_data['publisher_id'])
            book_obj.author.add(*validated_data['author_list'])
            return book_obj
    
        def update(self, instance, validated_data):
            # instance 更新的book_obj 对象
            #         # validated_data 校验通过的数据
            #         # ORM做更新操作
            instance.title = validated_data.get('title', instance.title)
            instance.pub_time = validated_data.get('pub_time', instance.pub_time)
            instance.category = validated_data.get('post_category', instance.category)
            instance.publisher_id = validated_data.get('publisher_id', instance.publisher_id)
            if validated_data.get('author_list'):
                instance.author.set(validated_data['author_list'])
            instance.save()
            return instance
    put请求serializers.py
    class BookEditView(APIView):
        def get(self, request, id):
            book_obj = models.Book.objects.filter(id=id).first()
            ser_obj = BookSerializers(book_obj)
            return Response(ser_obj.data)
    
        def put(self, request, id):
            book_obj = models.Book.objects.filter(id=id).first()
            ser_obj = BookSerializers(instance=book_obj, data=request.data, partial=True)  # partial=True部分校验
            if ser_obj.is_valid():
                ser_obj.save()
                return Response(ser_obj.validated_data)
            return Response(ser_obj.errors)
    put请求views.py

     验证

    如果我们需要对一些字段进行自定义的验证~DRF也给我们提供了钩子方法~~

    class BookSerializer(serializers.Serializer):
        id = serializers.IntegerField(read_only=True)
        title = serializers.CharField(max_length=32)
    
         def validate_title(self, value):
            if "python" not in value.lower():
                raise serializers.ValidationError("标题必须含有Python")
            return value
    单个字段校验
       def validate(self, attrs):
    #         print(33333)
    #         # attrs 字典有你传过来的所有的字段
    #         print(attrs)
    #         if "python" in attrs["title"].lower() or attrs["post_category"] == 1:
    #             return attrs
    #         else:
    #             raise serializers.ValidationError("分类或标题不合符要求")
    多个字段的验证
    def my_validate(value):
        if "敏感" in value.lower():
            raise serializers.ValidationError("包含敏感词汇,请重新提交")
        return value
    
    class BookSerializer(serializers.Serializer):
        id = serializers.IntegerField(read_only=True)
        title = serializers.CharField(max_length=32, validators=[my_validate]) # validators=[my_validate]自定义校验
    自定义校验

     

    ModelSerializer

    现在我们已经清楚了Serializer的用法,会发现我们所有的序列化跟我们的模型都紧密相关~

    那么,DRF也给我们提供了跟模型紧密相关的序列化器~~ModelSerializer~~

      -- 它会根据模型自动生成一组字段

      -- 它简单的默认实现了.update()以及.create()方法

    定义一个ModelSerializer序列化器

    class BookSerializer(serializers.ModelSerializer):
        class Meta:
            model = Book
            fields = "__all__"
            # fields = ["id", "title", "pub_time"]
            # exclude = ["user"]
            # 分别是所有字段 包含某些字段 排除某些字段
    
    定义ModelSerializer
    定义ModelSerializer

    外键关系的序列化

    注意:当序列化类MATE中定义了depth时,这个序列化类中引用字段(外键)则自动变为只读

    class BookSerializer(serializers.ModelSerializer):
        class Meta:
            model = Book
            fields = "__all__"
            # fields = ["id", "title", "pub_time"]
            # exclude = ["user"]
            # 分别是所有字段 包含某些字段 排除某些字段
            depth = 1
    # depth 代表找嵌套关系的第几层
    
    外键关系序列化
    外键关系序列化

    Meta中其它关键字参数

    class BookSerializer(serializers.ModelSerializer):
        chapter = serializers.CharField(source="get_chapter_display", read_only=True)
    
        class Meta:
            model = Book
            fields = "__all__"
            # fields = ["id", "title", "pub_time"]
            # exclude = ["user"]
            # 分别是所有字段 包含某些字段 排除某些字段
            depth = 1 #depth基本不用,外键关系向下找1层,会让你这些所有的外键关系变成read_only = True
            read_only_fields = ["id"]
            extra_kwargs = {"title": {"validators": [my_validate,]}}
    
    Meta中参数
    Meta中参数

    SerializerMethodField

    外键关联的对象有很多字段我们是用不到的~都传给前端会有数据冗余~就需要我们自己去定制序列化外键对象的哪些字段~~

    class BookSerializer(serializers.ModelSerializer):
        chapter = serializers.CharField(source="get_chapter_display", read_only=True)
        user = serializers.SerializerMethodField()
        publisher = serializers.SerializerMethodField()
    
        def get_user(self, obj):
            # obj是当前序列化的book对象
            users_query_set = obj.user.all()
            return [{"id": user_obj.pk, "name": user_obj.name} for user_obj in users_query_set]
    
        def get_publisher(self, obj):
            publisher_obj = obj.publisher
            return {"id": publisher_obj.pk, "title": publisher_obj.title}
    
        class Meta:
            model = Book
            fields = "__all__"
            # fields = ["id", "title", "pub_time"]
            # exclude = ["user"]
            # 分别是所有字段 包含某些字段 排除某些字段
            read_only_fields = ["id"]
            extra_kwargs = {"title": {"validators": [my_validate,]}}
    
    SerializerMethodField
    SerializerMethodField

    ModelSerializer完整版

    class BookSerializers(serializers.ModelSerializer):
        # 重写正序字段
        publisher_info = serializers.SerializerMethodField()  # 方法字段
        author_info = serializers.SerializerMethodField()
        category_info = serializers.SerializerMethodField()
    
        def get_publisher_info(self, obj):  # get+字段名就是一个钩子函数
            # obj 就是视图传过来的 序列化的每个Book对象
            publisher_obj = obj.publisher
            return {"id": publisher_obj.id, 'title': publisher_obj.title}
    
        def get_author_info(self, obj):
            author_obj = obj.author.all()  # 多对多
            return [{"id": author.id, "name": author.name} for author in author_obj]
    
        def get_category_info(self, obj):
            return obj.get_category_display()  # 多选字段调用display()方法进行返回
    
        class Meta:
            # 反序用默认的
            model = models.Book
            fields = "__all__"
            # depth = 1  # depth 基本不用,外键关系向下找1层,会让你这些所有的外键关系变成read_only = True
            extra_kwargs = {"publisher": {"write_only": True}, "author": {"write_only": True},"category":{"write_only": True}}
    ModelSerializer
    幻想毫无价值,计划渺如尘埃,目标不可能达到。这一切的一切毫无意义——除非我们付诸行动。
  • 相关阅读:
    使用putty上传文件到linux系统
    常用网页背景颜色
    如果你在Windows下用putty来远程连接Linux发现乱码请用下面的方法解决:
    如何让plsql查询的数据可编辑
    oracle中not in(null)问题
    linux学习之路第三天(vim和vi使用)
    linux菜鸡学习之路
    常用正则表达式()
    JAVA WEB 用servlet实现分页,思路比较清晰和简单。
    linux学习之路第三天
  • 原文地址:https://www.cnblogs.com/TodayWind/p/13886571.html
Copyright © 2020-2023  润新知