zoukankan      html  css  js  c++  java
  • DRF--ModelSerializer和时间格式化

    前戏

    在之前我们写序列化器的时候,写的很low,遇到反序列化的有时候还需要重写该字段,用post请求的时候,还要重写create方法,用put请求的时候,还需要重写update方法。总而言之,写起来很麻烦。来看看之前的是怎么写的

    class BookSerializer(serializers.Serializer):
        id = serializers.IntegerField(required=False)  # 只序列化,不走校验
        title = serializers.CharField(max_length=32, validators=[my_validate])
        pub_time = serializers.DateField()
        category = serializers.CharField(source="get_category_display", read_only=True)  # 只序列化用
        # 因为前端传的是数字,所以需要重写
        post_category = serializers.IntegerField(write_only=True)  # 只反序列化用
    
        publisher = PublisherSerializer(read_only=True)  # 一对多的表  只序列化用
        authors = AuthorSerializer(many=True, read_only=True)  # 多对多的表需要指定many=True 只序列化用
    
        publisher_id = serializers.IntegerField(write_only=True)  # 只反序列化用
        author_list = serializers.ListField(write_only=True)  # 只反序列化用
    
        def create(self, validated_data):
            # validated_data校验通过的数据
            # 通过ORM操作给book表增加数据
            book_obj = 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.authors.add(*validated_data['author_list'])  # 这个参数可能是一个列表
            return book_obj
    
        def update(self, instance, validated_data):
            # instance 更新的book_obj对象
            # validated_data 校验通过的数据
            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
    
        def validate_title(self, value):  # 对单一字段校验
            if "BDYJY" not in value.upper():
                return value
            raise serializers.ValidationError('标题里含有非法字符')  # 抛出错误
    
        def validate(self, attrs):  # 对多个字段校验
            # attrs是一个字典,里面是传过来的所有字段
            if 'python' in attrs['title'].lower() and attrs['post_category']==1:
                return attrs
            else:
                raise serializers.ValidationError('传的参数有误,请重新上传')
    from django.db import models
    
    # Create your models here.
    
    __all__ = ["Book", "Publisher", "Author"]
    
    
    class Book(models.Model):
        title = models.CharField(max_length=32)
        CHOICES = ((1, "python"), (2, "Liunux"), (3, "Go"))
        category = models.IntegerField(choices=CHOICES)
        pub_time = models.DateField()
        publisher = models.ForeignKey(to="Publisher")
        authors = models.ManyToManyField(to="Author")
    
    
    class Publisher(models.Model):
        title = models.CharField(max_length=32)
    
        def __str__(self):
            return self.title
    
    
    class Author(models.Model):
        name = models.CharField(max_length=32)
    
        def __str__(self):
            return self.name
    models.py

    ModelSerializer

    既然上面的写法很low,DRF提供给了我们ModelSerializer,之前序列化器继承的是serializers.Serializer,现在要继承serializers.ModelSerializer

    把之前的BookSerializer类里的东西删掉重写

    class BookSerializer(serializers.ModelSerializer):
    
        class Meta:
            model = Book  # 对应的表名
            fields = "__all__"  # 显示所有的字段
            # fields = ['id','title']  # 显示指定的字段
            # exclude = ['id', 'title']  # 不显示指定的字段

    这样我们访问这个接口查看数据

     会发现Choise字段和关联表的字段不是我们想要的,我们可以加上depth来让它显示成为我们想要的值,后面的值是表示几层,因为book表和author,publisher表都是一层关联,另外两者表没有一对多和多对一的关系,所以我们可以写成depth=1

    class BookSerializer(serializers.ModelSerializer):
    
        class Meta:
            model = Book  # 对应的表名
            fields = "__all__"  # 显示所有的字段
            # fields = ['id','title']  # 显示指定的字段
            # exclude = ['id', 'title']  # 不显示指定的字段
            depth = 1  # 会让你这些所有的外键关系变成read_only = True

    在来请求下这个接口

     这样我们虽然解决了一对一和多对多的字段,但是Chiose字段还不是显示我们想要的数据,而且depth会让你这些所有的外键关系变成read_only = True,所以实际开发中都不用,都是重写

     重写字段

    class BookSerializer(serializers.ModelSerializer):
        # 重写publisher字段
        publisher_info = serializers.SerializerMethodField()
        author_info = serializers.SerializerMethodField()
    
        def get_publisher_info(self, obj):
            # 函数名为 get_自定义的字段名
            # return了一个'ok',表示上面的publisher_info='ok',会返回给前端
            return 'ok'
    
        def get_author_info(self, obj):
            return 'yes'
    
        class Meta:
            model = Book  # 对应的表名
            fields = "__all__"  # 显示所有的字段
            # fields = ['id','title']  # 显示指定的字段
            # exclude = ['id', 'title']  # 不显示指定的字段

    其中的obj就是我们在views.py里序列化的每个对象,也就是下面的book_queryset

     ser_obj = BookSerializer(book_queryset, many=True)

    请求接口

     会发现我们重写的数据都返回给了调用这个接口的,所以我们可以自定义需要返回哪些字段。

    class BookSerializer(serializers.ModelSerializer):
        # 重写publisher字段
        publisher_info = serializers.SerializerMethodField()
        author_info = serializers.SerializerMethodField()
        category_display = serializers.SerializerMethodField()  
    
        def get_publisher_info(self, obj):
            # 函数名为 get_自定义的字段名
            publisher_obj = obj.publisher  ForeignKey,拿到的是publisher表的对象
            return {"id":publisher_obj.id,'title':publisher_obj.title}
    
        def get_author_info(self, obj):
            authors_queryset = obj.authors.all()  ManyToMany,.all拿到的是queryset里所有的对象
            return [{'id':author.id,'name':author.name} for author in authors_queryset]
    
        def get_category_display(self, obj):
            return obj.get_category_display()    # 调用这个方法,会返回汉字
    
        class Meta:
            model = Book  # 对应的表名
            fields = "__all__"  # 显示所有的字段
            # fields = ['id','title']  # 显示指定的字段
            # exclude = ['id', 'title']  # 不显示指定的字段

    publisher_info = serializers.SerializerMethodField() 叫做方法字段,需要一个方法,把方法里的返回值赋值给该字段

    上面我们自定义的字段已经显示了,然而之前的字段我们可以让它不显示,在Meta加个 extra_kwargs ,里面的是个字典,key为不显示的字段名,value如果是write_only为True,就不显示了

    class BookSerializer(serializers.ModelSerializer):
        # 重写publisher字段
        publisher_info = serializers.SerializerMethodField(read_only=True)
        author_info = serializers.SerializerMethodField(read_only=True)
        category_display = serializers.SerializerMethodField(read_only=True)  # Choise字段,后面必须是display
    
        def get_publisher_info(self, obj):
            # 函数名为 get_自定义的字段名
            publisher_obj = obj.publisher
            return {"id":publisher_obj.id,'title':publisher_obj.title}
    
        def get_author_info(self, obj):
            authors_queryset = obj.authors.all()
            return [{'id':author.id,'name':author.name} for author in authors_queryset]
    
        def get_category_display(self, obj):
            return obj.get_category_display()
    
        class Meta:
            model = Book  # 对应的表名
            fields = "__all__"  # 显示所有的字段
            # fields = ['id','title']  # 显示指定的字段
            # exclude = ['id', 'title']  # 不显示指定的字段
            extra_kwargs = {'category':{"write_only":True},'publisher':{"write_only":True},
                            'authors':{"write_only":True}}

    这样,当我们使用post或者put方法时,就不需要再重写create方法和update方法了

    在函数里重写方法字段时,里面写了read_only=True,表示序列化时使用,而在Meta类里,默认的字段category,publisher,author为write_only,所以序列化的时候,这些字段不显示,显示的是重写的那些字段。反序列化时用的是extra_kwargs里的字段

    时间格式化

    如果我们在创建时间的时候,使用的是DateTimeField,类似于下面这样

    update_time = models.DateTimeField(auto_now=True, verbose_name="更新时间", help_text="更新时间")

    如果想返回的时候,直接格式化好,我们就可以在序列化的时候加上这句就可以了

    update_time = serializers.DateTimeField(format="%Y-%m-%d %H:%M:%S", required=False, read_only=True)

    但是只这样修改,返回的时间是UTC的时间,我们还需要在settings.py里更改,将原来的UTC时间改为国内的时间,找到LANGUAGE_CODE和TIME_ZONE,改为中国上海的时间,如下

    LANGUAGE_CODE = 'zh-hans'
    
    TIME_ZONE = 'Asia/Shanghai'

    总结

     ModelSerializer

      默认生成关联的模型表里的所有字段

      配置

                class Meta:

          model=表名

          fields="__all__"/["字段名",]

          exclude=["字段名",]

          depth=1 外键关系找一层 会让外键关系字段变成read_only=True

          extra_kwargs={"字段名": {配置的属性}}

      SerializerMethodField()

        方法字段 会调用它自己的钩子方法,把方法的返回值给字段

        def get_字段名(self, obj):

          循环序列化的每个模型对象就是obj

          对obj进行ORM操作

          return 自定义的数据结构

  • 相关阅读:
    spring-AnnotationConfigApplicationContext源码阅读
    图解http pdf
    paasone的创新(2):separated langsysdemo ecosystem及demo driven debug
    Plan9:一个从0开始考虑分布式,分布appmodel的os设计
    terra++
    qtcling
    terracling:前端metalangsys后端uniform backend免编程binding生成式语言系统设想
    ubuntu touch: deepin pc os和deepin mobile os的天然融合
    windows版gbc:基于enginx的组件服务器系统paas,可用于mixed web与websocket game
    WinPE VirtIO云主机版 支持west263 阿里云aliyun 送精简win2k3镜像
  • 原文地址:https://www.cnblogs.com/zouzou-busy/p/11563960.html
Copyright © 2011-2022 走看看