zoukankan      html  css  js  c++  java
  • 【2020Python修炼记】Django Rest Framework-2-drf序列化组件

    【目录】

    一、什么是序列化器

    二、最简单的 serializers.Serializer 序列化器的使用

    2.1 定义序列化器

    2.2 创建Serializer对象

    2.3 序列化器的使用

    2.3.1 序列化

    2.3.2 反序列化

    三、模型序列化器 ModelSerializer 的使用

     

    一、什么是序列化器

    作用:

    1. 序列化,序列化器会把模型对象转换成字典,经过response以后变成json字符串
    2. 反序列化,把客户端发送过来的数据,经过request以后变成字典,序列化器可以把字典转成模型
    3. 反序列化,完成数据校验功能

    二、最简单的 serializers.Serializer 序列化器的使用

    准备工作——

    =创建子应用 sers

    python manage.py startapp sers

    =准备模型表(记得操作-数据库迁移,以及新增基础数据)

    from django.db import models
    
    # Create your models here.
    
    class Student(models.Model):
        # 模型字段
        name = models.CharField(max_length=100,verbose_name="姓名",help_text="提示文本:账号不能为空!")
        sex = models.BooleanField(default=True,verbose_name="性别")
        age = models.IntegerField(verbose_name="年龄")
        class_null = models.CharField(max_length=5,verbose_name="班级编号")
        description = models.TextField(verbose_name="个性签名")
    
        class Meta:
            db_table="tb_student"
            verbose_name = "学生"
            verbose_name_plural = verbose_name
    Student

     

    2.1 定义序列化器

    Django REST framework中的Serializer使用类来定义,须继承自rest_framework.serializers.Serializer

    注意:serializer不是只能为数据库模型类定义,也可以为非数据库模型类的数据定义。serializer是独立于数据库之外的存在。

    from rest_framework import serializers
    
    # 声明序列化器,所有的序列化器都要直接或者间接继承于 Serializer
    # 其中,ModelSerializer是Serializer的子类,ModelSerializer在Serializer的基础上进行了代码简化
    class StudentSerializer(serializers.Serializer):
        """学生信息序列化器"""
        # 1. 需要进行数据转换的字段
        id = serializers.IntegerField()
        name = serializers.CharField()
        age = serializers.IntegerField()
        sex = serializers.BooleanField()
        description = serializers.CharField()
    
        # 2. 如果序列化器集成的是ModelSerializer,则需要声明调用的模型信息
    
        # 3. 验证代码
    
        # 4. 编写添加和更新模型的代码

     

    常用字段类型:

    字段字段构造方式
    BooleanField BooleanField()
    NullBooleanField NullBooleanField()
    CharField CharField(max_length=None, min_length=None, allow_blank=False, trim_whitespace=True)
    EmailField EmailField(max_length=None, min_length=None, allow_blank=False)
    RegexField RegexField(regex, max_length=None, min_length=None, allow_blank=False)
    SlugField SlugField(maxlength=50, min_length=None, allow_blank=False) 正则字段,验证正则模式 [a-zA-Z0-9-]+
    URLField URLField(max_length=200, min_length=None, allow_blank=False)
    UUIDField UUIDField(format=’hex_verbose’) format: 1) 'hex_verbose' 如"5ce0e9a5-5ffa-654b-cee0-1238041fb31a" 2) 'hex'如 "5ce0e9a55ffa654bcee01238041fb31a" 3)'int' - 如: "123456789012312313134124512351145145114" 4)'urn' 如: "urn:uuid:5ce0e9a5-5ffa-654b-cee0-1238041fb31a"
    IPAddressField IPAddressField(protocol=’both’, unpack_ipv4=False, **options)
    IntegerField IntegerField(max_value=None, min_value=None)
    FloatField FloatField(max_value=None, min_value=None)
    DecimalField DecimalField(max_digits, decimal_places, coerce_to_string=None, max_value=None, min_value=None) max_digits: 最多位数 decimal_palces: 小数点位置
    DateTimeField DateTimeField(format=api_settings.DATETIME_FORMAT, input_formats=None)
    DateField DateField(format=api_settings.DATE_FORMAT, input_formats=None)
    TimeField TimeField(format=api_settings.TIME_FORMAT, input_formats=None)
    DurationField DurationField()
    ChoiceField ChoiceField(choices) choices与Django的用法相同
    MultipleChoiceField MultipleChoiceField(choices)
    FileField FileField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL)
    ImageField ImageField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL)
    ListField ListField(child=, min_length=None, max_length=None)
    DictField DictField(child=)

    选项参数:

    参数名称作用
    max_length 最大长度
    min_lenght 最小长度
    allow_blank 是否允许为空
    trim_whitespace 是否截断空白字符
    max_value 最小值
    min_value 最大值

    通用参数:

    参数名称说明
    read_only 表明该字段仅用于序列化输出,默认False
    write_only 表明该字段仅用于反序列化输入,默认False
    required 表明该字段在反序列化时必须输入,默认True
    default 反序列化时使用的默认值
    allow_null 表明该字段是否允许传入None,默认False
    validators 该字段使用的验证器
    error_messages 包含错误编号与错误信息的字典
    label 用于HTML展示API页面时,显示的字段名称
    help_text 用于HTML展示API页面时,显示的字段帮助提示信息
    【总结】
    
    1、字段属性
        title=serializers.CharField(max_length=32,min_length=2,read_only=True)
        price=serializers.DecimalField(max_digits=5, decimal_places=2)
        publish=serializers.CharField(max_length=32,write_only=True)
    
    =1、CharField 必须要有的字段
    max_length
    
    =2、DecimalField 必须要有的字段
    max_digits
    decimal_places
    
    =3、关于 read_only/write_only
    ===序列化/反序列化
    【序列化】 就是将对象转化方便传输和存储字节序列,例如json.dumps就是序列化(狭义的序列化,将字典转化为json字符串),
    这样得到的json字符串不仅直接可以在其他语言使用(跨平台比较好),而且可以在前后端进行传输交互(drf序列化器)
    
    【反序列化】 恰恰相反,而是将字节序列转化为对象,json.loads是将json字符串转化为字典,
    是狭义的反序列化(因为在python, 一切皆对象,字典是dict( )),
    而在项目中,前端传过来的序列化数据通过反序列化得到对象,进一步可以通过ORM操作,存入数据库。
    
    === read_only/write_only
    【read_only】表明该字段仅用于序列化输出,默认 False
    read_only 是只用于读,不能写(可以理解为只能从后台到前台,后台操作后直接传前台),
    不能对字段进行修改操作(也就是不和数据库进行交互),用在序列化字段里。
    例如数据表中,gender只用于传到前端展示。而不用于存到数据库
    
    【write_only】表明该字段仅用于反序列化输入,默认 False
    write_only是只写不能读,可以理解为只能前台到后台,
    在后台做了逻辑操作后直接存数据库,在反序列化字段使用。
    在数据表中,password就是反序列化,不需要传前台,
    而re_password是自定义反序列化字段,仅用作后台和密码进行比较,
    然后把结果返回前台,所以不存到数据库,在全局钩子使用时要pop掉
    
    【解说版2】
    read_only
     该字段仅用于 序列化输出(后端--》前端)
    (read_only=True)
    即 后端往前端 传送数据时(序列化),该字段只用于显示在前端;
    如果从前端往后端输入数据时(反序列化),该字段则可以不传值(这需要提前给该字段增加 null=True 属性,允许字段在反序列化输入时 可以为空,否则数据库会报错)
    
    write_only
    该字段仅用于 反序列化输入 (前端--》后端)
    (write_only=True)
    从前端往后端输入数据时(反序列化),该字段必须传值
    后端往前端 传送序列化数据时(序列化),该字段不会显示在前端
    
    用于 反序列化时必填,写入数据库的字段,但是不想后面在前端显示
    (一般若不想让字段显示在前端,是直接注释字段;但是这样可能会在反序列化时出问题,因此使用 write_only=True 来控制字段 )

     

    2.2 创建Serializer对象

    定义好Serializer类后,就可以创建Serializer对象了。

    Serializer的构造方法为:

    Serializer(instance=None, data=empty, **kwarg)

     

    说明:

    1)用于序列化时,将模型类对象传入instance参数

    2)用于反序列化时,将要被反序列化的数据传入data参数

    3)除了instance和data参数外,在构造Serializer对象时,还可通过context参数额外添加数据,如

    serializer = AccountSerializer(account, context={'request': request})

     

    通过context参数附加的数据,可以通过Serializer对象的context属性获取。

    1. 使用序列化器的时候一定要注意,序列化器声明了以后,不会自动执行,需要我们在视图中进行调用才可以。
    2. 序列化器无法直接接收数据,需要我们在视图中创建序列化器对象时把使用的数据传递过来。
    3. 序列化器的字段声明类似于我们前面使用过的表单系统。
    4. 开发restful api时,序列化器会帮我们把模型数据转换成字典.
    5. drf提供的视图会帮我们把字典转换成json,或者把客户端发送过来的数据转换字典.

     

    2.3 序列化器的使用

    序列化器的使用分两个阶段:

    1. 在客户端请求时,使用序列化器可以完成对数据的 反序列化(前端-----》后端)。
    2. 在服务器响应时,使用序列化器可以完成对数据的 序列化 (后端-----》前端)。

    2.3.1 序列化

    2.3.1.1 基本使用

    1) 先查询出一个学生对象

    from students.models import Student
    
    student = Student.objects.get(id=3)

     

    2) 构造序列化器对象

    from .serializers import StudentSerializer
    
    serializer = StudentSerializer(instance=student)

     

    3)获取序列化数据

    通过data属性可以获取序列化后的数据

    serializer.data

     

    完整视图代码:

    from django.views import View
    from students.models import Student
    from .serializers import StudentSerializer
    from django.http.response import JsonResponse
    class StudentView(View):
        """使用序列化器序列化转换单个模型数据"""
        def get(self,request,pk):
            # 获取数据
            student = Student.objects.get(pk=pk)
            # 数据转换[序列化过程]
            serializer = StudentSerializer(instance=student)
            print(serializer.data)
            # 响应数据
            return JsonResponse(serializer.data)

     

    4)如果要被序列化的是包含多条数据的查询集QuerySet,可以通过添加many=True参数补充说明

    """使用序列化器序列化转换多个模型数据"""
    def get(self,request):
        # 获取数据
        student_list = Student.objects.all()
    
        # 转换数据[序列化过程]
        # 如果转换多个模型对象数据,则需要加上many=True
        serializer = StudentSerializer(instance=student_list,many=True)
        print( serializer.data ) # 序列化器转换后的数据
    
        # 响应数据给客户端
        # 返回的json数据,如果是列表,则需要声明safe=False  #默认safe=True代表只能序列化字典对象,safe=False代表可以序列化字典以外的对象
       return JsonResponse(serializer.data,safe=False)

     

    2.3.1.1 高级使用—— source 和 serializers.SerializerMethodField()的用法

    source 用法

    source的用法1
    —— 修改字段前端显示的名字,真实的字段名是title,想让其在前端显示为name,则可以改写如下:
    注意 设定的前端显示名 不能与真实的字段名相同,否则会报错
    name=serializers.CharField(max_length=32,min_length=2,read_only=True,source='title')
    
    source的用法2
    —— 若表模型中 定义有方法,则可以将方法执行的返回值 传给source
    例如
    -1- models.py的book模型中,有个test方法
        def test(self):
            # python是强类型语言,不支持字符串和数字直接相加
            return self.title+str(self.price)
    
    -2- 则在序列化器类中的 需要序列化字段中 加上一个字段用于接收方法执行的返回值
        test_result=serializers.CharField(source='test')
    
    source的用法3 ——支持 xx.yy 的跨表操作
    例如
    -1- models.py --两张一对多关系的publish表模型和 book表模型
    book表模型中,加上关联字段
        publish = models.ForeignKey(to='Publish',null=True,on_delete=models.CASCADE)
    
    publish表模型
    class Publish(models.Model):
        name=models.CharField(max_length=32)
        addr=models.CharField(max_length=32)
        def __str__(self):
            return self.name
    
    book表模型的序列化器类-- source='publish.addr'
        publish_addr=serializers.CharField(max_length=32,source='publish.addr')  # objects.name

     

     serializers.SerializerMethodField()的用法

        # =1、需要指定使用SerializerMethodField()的字段
    # =2、搭配定义一个方法,规定 get_字段名,方法内写返回给字段的数据
    ### 取出图书的出版社详细信息(id,name,addr)
    class BookSerializer(serializers.Serializer):
        id = serializers.IntegerField(required=False)
        name = serializers.CharField(max_length=32,min_length=2,source='title')
        price = serializers.DecimalField(max_digits=5, decimal_places=2)
    # =1、需要指定使用SerializerMethodField()的字段 publish = serializers.SerializerMethodField() # =2、搭配定义一个方法,规定 get_字段名,方法内写返回给字段的数据 def get_publish(self,obj): dic={'name':obj.publish.name,'addr':obj.publish.addr} return dic # 这里返回什么,就序列化显示什么

     

     

    2.3.2 反序列化

    =1  数据验证

    使用序列化器进行反序列化时,需要对数据进行验证后,才能获取验证成功的数据或保存成模型类对象。

    在获取反序列化的数据前,必须调用is_valid()方法进行验证,验证成功返回True,否则返回False。

    验证失败,可以通过序列化器对象的errors属性获取错误信息,返回字典,包含了字段和字段的错误。如果是非字段错误,可以通过修改REST framework配置中的NON_FIELD_ERRORS_KEY来控制错误字典中的键名。

    验证成功,可以通过序列化器对象的validated_data属性获取数据。

    在定义序列化器时,指明每个字段的序列化类型和选项参数,本身就是一种验证行为。

    如我们前面定义过的BookInfoSerializer

    # models.py
    class
    BookInfoSerializer(serializers.Serializer): """图书数据序列化器""" id = serializers.IntegerField(label='ID', read_only=True) btitle = serializers.CharField(label='名称', max_length=20) bpub_date = serializers.DateField(label='发布日期', required=False) bread = serializers.IntegerField(label='阅读量', required=False) bcomment = serializers.IntegerField(label='评论量', required=False) image = serializers.ImageField(label='图片', required=False)

     

    通过构造序列化器对象,并将要反序列化的数据传递给data构造参数,进而进行验证

    # views.py
    
    from booktest.serializers import BookInfoSerializer
    
    data = {'bpub_date': 123}
    serializer = BookInfoSerializer(data=data)
    serializer.is_valid()  # 返回False
    serializer.errors
    # {'btitle': [ErrorDetail(string='This field is required.', code='required')], 
    'bpub_date': [ErrorDetail(string='Date has wrong format. Use one of these formats instead: YYYY[-MM[-DD]].', code='invalid')]}
    serializer.validated_data # {} data = {'btitle': 'python'} serializer = BookInfoSerializer(data=data) serializer.is_valid() # True serializer.errors # {} serializer.validated_data # OrderedDict([('btitle', 'python')])

     

    is_valid()方法还可以在验证失败时抛出异常serializers.ValidationError,可以通过传递raise_exception=True参数开启,

    REST framework接收到此异常,会向前端返回HTTP 400 Bad Request响应。

    # Return a 400 response if the data was invalid.
    serializer.is_valid(raise_exception=True)

     

     =2 局部钩子/全局钩子/ 自定义验证器

    如果觉得这些还不够,需要再补充定义验证行为,可以使用以下三种方法:

    1) 局部钩子 ——  validate_字段名

    <field_name>字段进行验证,如

    # serializer.py
    class
    BookInfoSerializer(serializers.Serializer): """图书数据序列化器""" ... def validate_btitle(self, value): if 'django' not in value.lower(): raise serializers.ValidationError("图书不是关于Django的") return value

    测试 views.py

    from booktest.serializers import BookInfoSerializer
    data = {'btitle': 'python'}
    serializer = BookInfoSerializer(data=data)
    serializer.is_valid()  # False   
    serializer.errors
    #  {'btitle': [ErrorDetail(string='图书不是关于Django的', code='invalid')]}

     

    2) 全局钩子—— validate

    在序列化器中需要同时对多个字段进行比较验证时,可以定义validate方法来验证,如

    class BookInfoSerializer(serializers.Serializer):
        """图书数据序列化器"""
        ...
    
        def validate(self, attrs):
            bread = attrs['bread']
            bcomment = attrs['bcomment']
            if bread < bcomment:
                raise serializers.ValidationError('阅读量小于评论量')
            return attrs

    测试

    from booktest.serializers import BookInfoSerializer
    data = {'btitle': 'about django', 'bread': 10, 'bcomment': 20}
    s = BookInfoSerializer(data=data)
    s.is_valid()  # False
    s.errors
    #  {'non_field_errors': [ErrorDetail(string='阅读量小于评论量', code='invalid')]}

     

    3) 自定义验证器方法—— validators 

    在字段中添加validators选项参数,也可以补充验证行为,如

    # serializer.py
    
    def about_django(value):
        if 'django' not in value.lower():
            raise serializers.ValidationError("图书不是关于Django的")
    
    class BookInfoSerializer(serializers.Serializer):
        """图书数据序列化器"""
        id = serializers.IntegerField(label='ID', read_only=True)
        btitle = serializers.CharField(label='名称', max_length=20, validators=[about_django])
        bpub_date = serializers.DateField(label='发布日期', required=False)
        bread = serializers.IntegerField(label='阅读量', required=False)
        bcomment = serializers.IntegerField(label='评论量', required=False)
        image = serializers.ImageField(label='图片', required=False)

    测试:

    from booktest.serializers import BookInfoSerializer
    data
    = {'btitle': 'python'} serializer = BookInfoSerializer(data=data) serializer.is_valid() # False serializer.errors # {'btitle': [ErrorDetail(string='图书不是关于Django的', code='invalid')]}

     

    =3  保存数据

    1前面的验证数据成功后,我们可以使用序列化器来完成数据反序列化的过程.这个过程可以把数据转成模型类对象.

    可以通过实现create()和update()两个方法来实现。

    class BookInfoSerializer(serializers.Serializer):
        """图书数据序列化器"""
        ...
    
        def create(self, validated_data):
            """新建"""
            return BookInfo(**validated_data)
    
        def update(self, instance, validated_data):
            """更新,instance为要更新的对象实例"""
            instance.btitle = validated_data.get('btitle', instance.btitle)
            instance.bpub_date = validated_data.get('bpub_date', instance.bpub_date)
            instance.bread = validated_data.get('bread', instance.bread)
            instance.bcomment = validated_data.get('bcomment', instance.bcomment)
            return instance

    如果需要在返回数据对象的时候,也将数据保存到数据库中,则可以进行如下修改

    class BookInfoSerializer(serializers.Serializer):
        """图书数据序列化器"""
        ...
    
        def create(self, validated_data):
            """新建"""
            return BookInfo.objects.create(**validated_data)
    
        def update(self, instance, validated_data):
            """更新,instance为要更新的对象实例"""
            instance.btitle = validated_data.get('btitle', instance.btitle)
            instance.bpub_date = validated_data.get('bpub_date', instance.bpub_date)
            instance.bread = validated_data.get('bread', instance.bread)
            instance.bcomment = validated_data.get('bcomment', instance.bcomment)
            instance.save()
            return instance

    实现了上述两个方法后,在反序列化数据的时候,就可以通过save()方法返回一个数据对象实例了

    book = serializer.save()

     

    2如果创建序列化器对象的时候,没有传递instance实例,则调用save()方法的时候,create()被调用,相反,如果传递了instance实例,则调用save()方法的时候,update()被调用。

    # views.py
    
    from db.serializers import BookInfoSerializer
    data = {'btitle': '封神演义'}
    serializer = BookInfoSerializer(data=data)
    serializer.is_valid()  # True
    serializer.save()  # <BookInfo: 封神演义>
    
    
    from db.models import BookInfo
    book = BookInfo.objects.get(id=2)
    data = {'btitle': '倚天剑'}
    serializer = BookInfoSerializer(book, data=data)
    serializer.is_valid()  # True
    serializer.save()  # <BookInfo: 倚天剑>
    book.btitle  # '倚天剑'

     

    附加说明

    1) 在对序列化器进行save()保存时,可以额外传递数据,这些数据可以在create()和update()中的validated_data参数获取到

    # request.user 是django中记录当前登录用户的模型对象
    serializer.save(owner=request.user)

     

    2)默认序列化器必须传递所有required的字段,否则会抛出验证异常。但是我们可以使用 partial 参数来允许部分字段更新

    # Update `comment` with partial data
    serializer = CommentSerializer(comment, data={'content': u'foo bar'}, partial=True)

     

     

    【总结-重难点】

    1、定义序列化器——序列化/反序列化  read_only / write_only

    2、创建Serializer对象——三大参数 instance / data / context

    3、使用序列化器——

    序列化— many=True /  safe=False

    反序列化— validators=[ ] / partial 参数 / is_valid() / source / serializers.SerializerMethodField()

     

    【练习代码笔记】

    from django.db import models
    
    # Create your models here.
    
    
    class Book(models.Model):
        id = models.AutoField(primary_key=True)
        title = models.CharField(max_length=32,null=True)
        price = models.DecimalField(max_digits=5, decimal_places=2)
        # publish = models.CharField(max_length=32)
        publish = models.ForeignKey(to='Publish',null=True,on_delete=models.CASCADE)
    
    
        class Meta:
            db_table = "tb_book"
            verbose_name = "书籍"
            verbose_name_plural = verbose_name    # 复数
    
        # source的用法2
        def test(self):
            # python是强类型语言,不支持字符串和数字直接相加
            return self.title+str(self.price)
    
    # source的用法3
    class Publish(models.Model):
        name=models.CharField(max_length=32)
        addr=models.CharField(max_length=32)
    
        # 若想让前端显示为字段具体内容,而不是对象,则需要重写该字段的 __str__方法
        # 也可以在序列化器类的序列化字段中,在该字段的属性 使用 source='publish.name'
    
        def __str__(self):
            return self.name
    
    
    # 1、db_column 指定了对应的字段名
    #    db_table 指定了对应的表名
    #
    # 2、verbose_name / verbose_name_plural  指定在admin管理界面中显示中文;
    # verbose_name表示单数形式的显示,
    # verbose_name_plural表示复数形式的显示;
    # 中文的单数和复数一般不作区别。
    
    # 3、help_text="字段提示文本,如'请输入密码'等等"
    models.py
    """drftest URL Configuration
    
    The `urlpatterns` list routes URLs to views. For more information please see:
        https://docs.djangoproject.com/en/2.2/topics/http/urls/
    Examples:
    Function views
        1. Add an import:  from my_app import views
        2. Add a URL to urlpatterns:  path('', views.home, name='home')
    Class-based views
        1. Add an import:  from other_app.views import Home
        2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home')
    Including another URLconf
        1. Import the include() function: from django.urls import include, path
        2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
    """
    
    from django.contrib import admin
    from django.urls import path,include,re_path
    from test2_sers import views
    
    urlpatterns = [
        path('admin/', admin.site.urls),
    # 把子应用test1 中的路由文件加载到总路由文件中
        path('student/',include('test1.urls')),
    
    # <1>【接口1:get-查询所有】 朝该地址发送 get请求,会拿到所有数据/
    # <3>【接口3:新增一个   Book--------》post
        path('books/',views.Book.as_view()),
    
    # <2>【接口2:get-查询单个】
        re_path('^books/(?P<id>d+)',views.BookDetail.as_view()),
    
    ]
    
    
    
    
    # -----------------------------------------》
    # 【-总结-】
    # 【关于 模型表.as_view() 】
    # 所有基于类的视图都必须继承View类,该类用来处理视图关联到URL的操作,具体分析如下:
    #
    # 由于django的URL解析器期望发送request以及相关参数给一个可调用的函数,而不是一个类,
    # 所以基于类的视图有一个as_view()类方法(该方法继承自父类View),调用该方法会返回URL解析器所期望的函数,
    # 该函数会在请求到达与关联模式匹配的URL时调用,就像调用视图函数一个样子。查看源码会发现调用该函数首先会创建一个MyView类的实例,然后
    #
    # 1、调用self.setup()方法,用于初始化一系列属性。
    # 2、之后调用self.dispatch(),该方法查看request以确定本次请求是GET还是POST等,如果定义了匹配方法,则将请求转发给匹配方法;
    # 如果没有,则引发HttpResponseNotAllowed。本例中将调用我们自定义的get与post方法来响应请求
    #
    # ps:基于面向对象的知识,上述方法,如果子类MyView未重写,则去父类View中寻找
    urls.py
    from django.shortcuts import render
    
    # Create your views here.
    
    from django.views import View
    
    from rest_framework.views import APIView
    from rest_framework.request import Request
    from rest_framework.response import Response
    
    from test2_sers import models
    from test2_sers.serializers2 import BookSerializer
    
    # from app应用名.序列化器类的py文件 import 自定义序列化器
    
    '''
    序列化器的使用--写五个接口
    -<1>【接口1:查询所有   Book--------》get
    -<2>【接口2:查询单个   BookDetail--》get  
    -<3>【接口3:新增一个   Book--------》post
    -<4>【接口4:删除一个   BookDetail--》delete
    -<5>【接口5:修改一个   BookDetail--》put
    
    '''
    
    
    # CBV
    
    # 1、获取多条数据
    class Book(APIView):
        def get(self, request, *args, **kwargs):
            # \取出所有数据(queryset对象)
            res = models.Book.objects.all()
    
            # \创建序列化器对象—— BookSerializer对象
            # = 借助序列化器——实例化 自定义的序列化器类,得到序列化器对象
            ser = BookSerializer(instance=res, many=True)
            # print(type(ser))  # <class 'rest_framework.serializers.ListSerializer'>
    
            # \ 拿到了数据(queryset对象),通过序列化器对象处理后,得到的是字典
            # ser.data
            # 数据字典再经过 drf 的 Response,返回序列化好的json格式字符串,传给前端
            return Response(ser.data)
    
        # 3新增单个——使用最基础的序列化器,若要新增 必须重写create方法;若要修改,则必须重写 update方法
        def post(self,request):  # 关于post请求的数据格式-解说,见下方【总结】
            # post 提交的数据 都在request.data,且是个字典形式
            # <QueryDict: {'title': ['走去乡下过日子'], 'price': ['25'], 'publish': ['新世界出版社']}>
            print(request.data)
            # return Response('get it')
    
            # 使用序列化器进行反序列化时,需要对数据进行验证后,才能获取验证成功的数据或保存成模型类对象。
            ser=BookSerializer(data=request.data)  # 将需要反序列化的数据传给 data
            if ser.is_valid():  # 校验数据是否合法
                ser.save()      # 保存到数据库中----》运行,无法保存(因为未重写create/update方法)---》解决方法:在序列化器里 重写create方法
                return Response(ser.data)  # 保存成功 返回新增的当前对象
            else:
                # 没有校验通过的错误信息
                return Response(ser.errors)
    
    
    
    class BookDetail(APIView):
        # 2、获取单个数据对象
    
        # def get(self, request, id, *args, **kwargs):  # 传参方式,两种都可以
        def get(self, request, id):
            res = models.Book.objects.all().filter(id=id).first()
            # 单个,去掉many=True
            # 加many=True和不加,ser不是同一个对象o~~
            ser = BookSerializer(instance=res)
            # print(type(ser))   # <class 'test2_sers.serializers2.BookSerializer'>
            return Response(ser.data)
    
        # 4修改单个对象
        def put(self,request,id):
            # 通过id取到对象
            res={'code':100,'msg':''}
    
            try:
                book=models.Book.objects.get(id=id)  # 不要犯错只写id
                ser=BookSerializer(instance=book,data=request.data)
                ser.is_valid(raise_exception=True)
                ser.save()
                res['msg']='修改成功'
                res['result']=ser.data
    
            except Exception as e:
                res['code']=101
                res['msg']=str(e)
    
            return Response(res)
    
        # 5删除单个
        def delete(self,request,id):
            response={'code':100,'msg':'删除成功'}  # 可根据需求 增加判断条件
            models.Book.objects.filter(id=id).delete()
            return Response(response)
    
    
    
    # 【总结】
    # -----------------------------------
    # 1、序列化器的作用——drf的序列化/反序列化
    # =1.
    # 序列化, 序列化器会把模型对象转换成字典, 经过drf的Response以后变成json字符串
    # -Book - -序列化器 - -->字典 - -通过drf: Response - -》json格式字符串 - -->传给前端
    
    # =2.
    # 反序列化, 把客户端发送过来的数据, 经过request以后变成字典, 序列化器可以把字典转成模型
    # json格式数据 - --drf:Request -->字典 - --序列化器 - --》Book
    
    # =3.
    # 反序列化, 完成数据校验功能
    
    
    # -----------------------------------
    # 2、序列化器对象的参数说明
    # ser=BookSerializer(instance=res,many=True)
    
    # =1、instance=数据(queryset对象)
    # 序列化时,将模型类对象传入instance参数
    
    # =2、many=True
    # = 如果结果是多条数据(多个对象),则要加上 many=True
    # 如果是单个对象,则不写
    
    # =3、其他参数
    # data
    # 反序列化时,将要被反序列化的数据传入data参数
    # eg: serializer = Serializer(instance=None, data=empty, **kwarg)
    
    # context
    # 在构造Serializer对象时,还可通过context参数额外添加数据
    # eg: serializer = AccountSerializer(account, context={'request': request})
    
    # -----------------------------------
    # 拓展:
    # 接口的幂等性
    # post 请求不是接口幂等的,每次请求都是不一样的,就算是请求同一数据
    # put get 请求是接口幂等的,即多次操作得到的结果和单次操作的结果 是一样的
    
    # -----------------------------------
    # 【post请求的数据格式】
    
    # 一.HttpRequest.body
    #   1.1 当浏览器基于http协议的GET方法提交数据时(没有请求体一说),数据会按照k1=v1&k2=v2&k3=v3的格式放到url中,然后发送给django,django会将这些数据封装到request.GET中,注意此时的请求体request.body为空、无用
    #
    #   1.2 当浏览器基于http协议的POST方法提交数据时,数据会被放到请求体中发送给django,django会将接收到的请求体数据存放于HttpRequest.body属性中.
    #   但该属性的值为Bytes类型(套接字数据传输都是bytes类型),而通常情况下直接处理Bytes、并从中提取有用数据的操作是复杂而繁琐的,好在django会对它做进一步的处理与封装以便我们更为方便地提取数据,具体如何处理呢?
    #
    #   当前端采用POST提交数据时,数据有三种常用编码格式,编码格式不同Django会有不同的处理方式
    #   # 编码格式1:application/x-www-form-urlencoded,是form表单默认编码格式
    #   # 编码格式2:multipart/form-data,上传文件专用格式
    #   # 编码格式2:application/json,提交jason格式字符串
    #
    #   #====》I: 当POST数据的编码格式为application/x-www-form-urlencoded时《====
    #       HttpRequest.body中的数据格式为b'a=1&b=2&c=3'
    #       django会将其提取出来封装到request.POST中
    #       request.FILES此时为空
    #
    #       如:
    #       print(request.body) # b'a=1&b=2&c=3'
    #       print(request.POST) # <QueryDict: {'a': ['1'],'b': ['2'],'c': ['3']}
    #       print(request.FILES) # <MultiValueDict: {}>
    #
    #
    #   #====》II: 当POST数据的编码格式为multipart/form-data时《====
    #       详见:https://my.oschina.net/cnlw/blog/168466?fromerr=aQL9sTI2
    #
    #       HttpRequest.body中的数据格式为b'------WebKitFormBoundaryKtcwuksQltpNprep
    Content-Disposition: form-data;......',注意,文件部分数据是流数据,所以不在浏览器中显示是正常的
    #       django会将request.body中的非文件数据部分提取出来封装到request.POST中
    #       将上传的文件部分数据专门提取出来封装到request.FILES属性中
    #
    #       如:
    #       print(request.body) # 不要打印它,打印则报错,因为它是数据流
    #       print(request.POST) # <QueryDict: {'a': ['1'],'b': ['2'],'c': ['3']}
    #       print(request.FILES) # <MultiValueDict: {'head_img': [<InMemoryUploadedFile: 11111.jpg (image/jpeg)>]}>
    #
    #       强调:
    #         1、毫无疑问,编码格式2的数据量要大于编码格式1,如果无需上传文件,还是推荐使用更为精简的编码格式1
    #         2、FILES will only contain data if the request method was POST and the <form> that posted to the request had enctype="multipart/form-data". Otherwise, FILES will be a blank dictionary-like object.
    #
    #   #===》III: 当POST数据的编码格式为application/json时《====
    #       此时在django后台,request.POST和request.FILES中是没有值的,都放到request.body中了,需要用json.loads对其进行反序列化
    #
    #       如:
    #       print(request.body) # b'{"a":1,"b":2,"c":3}'
    #       print(request.POST) # <QueryDict: {}>
    #       print(request.FILES) # <MultiValueDict: {}>
    #
    #
    #   1.3 如何设定POST提交数据的编码格式
    #     前端往后台POST提交数据,常用技术有form表单和ajax两种
    #     form表单可以设置的数据编码格式有:编码格式1、编码格式2
    #     ajax可以设置的数据编码格式有:编码格式1、编码格式2、编码格式3
    #
    #     form表单可以通过属性enctype进行设置编码格,如下
    #       编码格式1(默认的编码格式):enctype="application/x-www-form-urlencoded"
    #       编码格式2(使用form表单上传文件时只能用该编码):enctype="multipart/form-data"
    子应用的views.py
    # 序列化器类---用于序列化Book表
    
    # 导入序列化器
    from rest_framework import serializers
    # 导入异常-校验
    from rest_framework.exceptions import ValidationError
    from test2_sers import models
    
    # 自定义校验规则,可以添加到序列化字段的validators属性
    def check(data):
        if len(data)>10:
            raise ValidationError('最长不能超过10')
        else:
            return data
    
    class BookSerializer(serializers.Serializer):
        # 1、列出要序列化的字段
        id = serializers.IntegerField(required=False)  # required=False 可以不传值
        # title=serializers.CharField(max_length=32,min_length=2,read_only=True)
        # source的用法1 ——修改字段前端显示的名字,真实的字段名是title,想让其在前端显示为name,则可以改写如下:
        name=serializers.CharField(max_length=32,min_length=2,read_only=True,source='title')
    
        price=serializers.DecimalField(max_digits=5, decimal_places=2)
        # 2、为字段增加校验规则 validators=[check,]
        # write_only=True 序列化时 不显示
        # publish=serializers.CharField(max_length=32,write_only=True,validators=[check,])
        # source的用法3 使用.的方法 获取字段数据
        publish_addr=serializers.CharField(max_length=32,source='publish.addr')  # objects.name
    
        # source的用法2 返回表模型中 方法的执行返回结果
        test_result=serializers.CharField(source='test')
    
    
    
        # 为什么要重写create?——新增数据
        # 因为继承了 serializers.Serializer,若想保存 新增的目标模型表的数据 就需要重写create
        def create(self, validated_data):
            res=models.Book.objects.create(**validated_data)
            print(res)
            return res
    
        # 重写 update方法 ——更新数据
        def update(self, instance, validated_data):   # instance 修改的对象,例如 book对象; validated_data 通过校验的反序列化的数据
            instance.title=validated_data.get('title')
            instance.price=validated_data.get('price')
            instance.publish=validated_data.get('publish')
            instance.save()   # 这个save() 不是drf的,是对象自己的
            return instance  # 一定要返回修改的数据对象
    
    
    
        # 局部钩子: validate_字段名
        # 需要带一个data,data就是该字段的数据
        # 例如 给 title字段增加一个局部钩子
        def validate_title(self,data):
            if data.startswith('sb'):
                raise ValidationError('不能以sb开头')
            else:
                return data
    
        # 全局钩子
        def validate(self, attrs):   # attrs 是全部数据
            # title=attrs['title']  # 字典取值 可以用key 也可以用 get
            title=attrs.get('title')
            publish=attrs.get('publish')
            if title==publish:
                raise ValidationError('书名不能跟出版社同名')
            else:
                return attrs
    
    
    
    
    
    
    
    # 【总结】
    #
    # 1、字段属性
    #     title=serializers.CharField(max_length=32,min_length=2,read_only=True)
    #     price=serializers.DecimalField(max_digits=5, decimal_places=2)
    #     publish=serializers.CharField(max_length=32,write_only=True)
    #
    # =1、CharField 必须要有的字段
    # max_length
    #
    # =2、DecimalField 必须要有的字段
    # max_digits
    # decimal_places
    #
    # =3、关于 read_only/write_only
    # ===序列化/反序列化
    # 【序列化】 就是将对象转化方便传输和存储字节序列,例如json.dumps就是序列化(狭义的序列化,将字典转化为json字符串),
    # 这样得到的json字符串不仅直接可以在其他语言使用(跨平台比较好),而且可以在前后端进行传输交互(drf序列化器)
    #
    # 【反序列化】 恰恰相反,而是将字节序列转化为对象,json.loads是将json字符串转化为字典,
    # 是狭义的反序列化(因为在python, 一切皆对象,字典是dict( )),
    # 而在项目中,前端传过来的序列化数据通过反序列化得到对象,进一步可以通过ORM操作,存入数据库。
    #
    # === read_only/write_only
    # 【read_only】表明该字段仅用于序列化输出,默认 False
    # read_only 是只用于读,不能写(可以理解为只能从后台到前台,后台操作后直接传前台),
    # 不能对字段进行修改操作(也就是不和数据库进行交互),用在序列化字段里。
    # 例如数据表中,gender只用于传到前端展示。而不用于存到数据库
    #
    # 【write_only】表明该字段仅用于反序列化输入,默认 False
    # write_only是只写不能读,可以理解为只能前台到后台,
    # 在后台做了逻辑操作后直接存数据库,在反序列化字段使用。
    # 在数据表中,password就是反序列化,不需要传前台,
    # 而re_password是自定义反序列化字段,仅用作后台和密码进行比较,
    # 然后把结果返回前台,所以不存到数据库,在全局钩子使用时要pop掉
    #
    # 【解说版2】
    # read_only
    #  该字段仅用于 序列化输出(后端--》前端)
    # (read_only=True)
    # 即 后端往前端 传送数据时(序列化),该字段只用于显示在前端;
    # 如果从前端往后端输入数据时(反序列化),该字段则可以不传值(这需要提前给该字段增加 null=True 属性,允许字段在反序列化输入时 可以为空,否则数据库会报错)
    #
    # write_only
    # 该字段仅用于 反序列化输入 (前端--》后端)
    # (write_only=True)
    # 从前端往后端输入数据时(反序列化),该字段必须传值
    # 后端往前端 传送序列化数据时(序列化),该字段不会显示在前端
    #
    # 用于 反序列化时必填,写入数据库的字段,但是不想后面在前端显示
    # (一般若不想让字段显示在前端,是直接注释字段;但是这样可能会在反序列化时出问题,因此使用 write_only=True 来控制字段 )
    
    
    # 2、source的用法
    # source的用法1
    # —— 修改字段前端显示的名字,真实的字段名是title,想让其在前端显示为name,则可以改写如下:
    # 注意 设定的前端显示名 不能与真实的字段名相同,否则会报错
    # name=serializers.CharField(max_length=32,min_length=2,read_only=True,source='title')
    #
    # source的用法2
    # —— 若表模型中 定义有方法,则可以将方法执行的返回值 传给source
    # 例如
    # -1- models.py的book模型中,有个test方法
    #     def test(self):
    #         # python是强类型语言,不支持字符串和数字直接相加
    #         return self.title+str(self.price)
    #
    # -2- 则在序列化器类中的 需要序列化字段中 加上一个字段用于接收方法执行的返回值
    #     test_result=serializers.CharField(source='test')
    #
    # source的用法3 ——支持 xx.yy 的跨表操作
    # 例如
    # -1- models.py --两张一对多关系的publish表模型和 book表模型
    # book表模型中,加上关联字段
    #     publish = models.ForeignKey(to='Publish',null=True,on_delete=models.CASCADE)
    #
    # publish表模型
    # class Publish(models.Model):
    #     name=models.CharField(max_length=32)
    #     addr=models.CharField(max_length=32)
    #     def __str__(self):
    #         return self.name
    #
    # book表模型的序列化器类-- source='publish.addr'
    #     publish_addr=serializers.CharField(max_length=32,source='publish.addr')  # objects.name
    #
    子应用的serializers.py

     

     

    三、模型序列化器 ModelSerializer 的使用 

    1、模型序列化器 ModelSerializer 的使用

    如果我们想要使用序列化器对应的是Django的模型类,DRF为我们提供了ModelSerializer模型类序列化器来帮助我们快速创建一个Serializer类。

    ModelSerializer与常规的Serializer相同,但提供了:

    • 基于模型类自动生成一系列字段
    • 基于模型类自动为Serializer生成validators,比如unique_together
    • 包含默认的create()和update()的实现

    1 原来用的Serilizer跟表模型没有直接联系, 模型类序列化器ModelSerilizer,跟表模型有对应关系

    2 使用——模板 

    class BookModelSerializer(serializers.ModelSerializer):
            class Meta:
                model=表模型    # 跟哪个表模型建立关系
                fields=[字段,字段] # 序列化的字段,反序列化的字段
                fields='__all__' # 所有字段都序列化,反序列化
                exclude=[字段,字段] # 排除哪些字段(不能跟fields同时使用)
                read_only_fields=['price','publish']  # 序列化显示的字段
                write_only_fields=['title']           # 反序列化需要传入的字段
                extra_kwargs ={'title':{'max_length':32,'write_only':True}}
                depth=1  # 了解,跨表1查询,最多建议写3
            # 重写某些字段
            publish = serializers.CharField(max_length=32,source='publish.name')
            # 局部钩子,全局钩子,跟原来完全一样

     

    3 新增,修改
    -统统不用重写create和update方法了,在ModelSerializer中重写了create和update

     

    2、serializers.SerializerMethodField()的用法

     用法跟在 serializers.Serializer 里的一样

    class BookModelSerializer(serializers.ModelSerializer):
        class Meta:
            model = models.Book
            fields = '__all__'
      # 注意 不要写在了Meta类中!! publish = serializers.SerializerMethodField() def get_publish(self,obj): dic={'name':obj.publish.name,'addr':obj.publish.addr} return dic

     

    3、序列化器类的嵌套——跨表获取数据

    # 3 模型序列化器类的嵌套
    class PublishModelSerializer(serializers.ModelSerializer):
        class Meta:
            model=models.Publish
            # fields='__all__'
            fields=['name','addr']
    
    class BookModelSerializer(serializers.ModelSerializer):
        class Meta:
            model = models.Book
            fields = '__all__'
    
        # publish = serializers.SerializerMethodField()
        # def get_publish(self,obj):
        #     dic={'name':obj.publish.name,'addr':obj.publish.addr}
        #     return dic
        # 序列化类的嵌套——会按照嵌套的序列化类的规则,序列化目标字段
        publish = PublishModelSerializer()

     4、Response的应用——自定义响应体类

    class CommonResponse():
        def __init__(self):
            self.code=100   # 在调用该方法的序列化器类中,若code值变了,则修改;没有则新增/按默认值
            self.msg=''
    
        @property  # 将方法包装成数据属性,使用不需加括号,直接通过 对象.属性
        def get_dic(self):
            return self.__dict__
    comResponse.py
    # # 自定义Response类
    # 数据操作的响应信息 写起来有些繁琐,可以自己定义响应体返回的数据的类,实例化得到对象 返回即可
    # # 封装2---也可以自己封装一个response,继承drf 的 Response
    
    # 封装1
    # 新建python文件 comResponse.py,创建 CommonResponse类
    # =1= 需要使用该类 先导入
    from myModelSerializer.comResponse import CommonResponse
    class BookDetail(APIView):
        def get(self, request, id):
            res = models.Book.objects.all().filter(id=id).first()
            ser = BookSerializer(instance=res)
            return Response(ser.data)
    
        def put(self, request, id):
            # =2= 实例化响应类 得到对象
            res=CommonResponse()
            try:
                book = models.Book.objects.get(id=id)
                ser = BookSerializer(instance=book, data=request.data)
                ser.is_valid(raise_exception=True)
                ser.save()
                # res['msg'] = '修改成功'
                # res['result'] = ser.data
                # =3= 对象.属性 获取响应信息
                res.code=100
                res.msg='修改成功'
                res.result=['hello','hi']  # 新增的数据/按照字典新增的方法
    
            except Exception as e:
                # res['code'] = 101
                # res['msg'] = str(e)
                res.code=101
                res.msg='未知错误'
    
            return Response(res.get_dic)
    
        def delete(self,request,id):
            response = {'code': 100, 'msg': '删除成功'}
            models.Book.objects.filter(id=id).delete()
            return Response(response)
    
    
    # ------------------------------------------------------
    # Response 属性
    # 响应状态码 status/content/等
    
    from rest_framework.status import HTTP_201_CREATED
    from rest_framework.settings import DEFAULTS  # 默认的drf控制界面的模板显示形式--JSON/浏览器
    
    # 局部配置-drf控制界面的模板显示形式,粒度更小,仅用于局部;其余的未配置的则使用settings.py里的全局配置
    from  rest_framework.renderers import JSONRenderer,BrowsableAPIRenderer   # 局部配置-step1
    
    class BookDetail(APIView):
    
        renderer_classes=[BrowsableAPIRenderer,]  # 局部配置-step2
    
        def get(self, request, id):
            res = models.Book.objects.all().filter(id=id).first()
            ser = BookSerializer(instance=res)
            return Response(ser.data)
    
        def put(self, request, id):
            res=CommonResponse()
            try:
                book = models.Book.objects.get(id=id)
                ser = BookModelSerializer(instance=book, data=request.data)
                ser.is_valid(raise_exception=True)
                ser.save()
                res.msg='成功'
                res.result=['sdsasdf','asdfa']
    
            except Exception as e:
                print(str(e))
                res.msg = '未知错误'
                res.code = 101
    
            return Response(data=res.get_dic,status=HTTP_201_CREATED)
        # Response(data=res.get_dic,status=HTTP_201_CREATED) 读源码/status.py
    
        def delete(self,request,id):
            response = {'code': 100, 'msg': '删除成功'}
            models.Book.objects.filter(id=id).delete()
            return Response(response)
    子应用-views.py

    【总结-练习代码】

    from django.db import models
    
    # Create your models here.
    
    class Book(models.Model):
        id = models.AutoField(primary_key=True)
        title = models.CharField(max_length=32,null=True)
        price = models.DecimalField(max_digits=5, decimal_places=2,null=True)
        publish = models.ForeignKey(to='Publish',null=True,on_delete=models.CASCADE)
    
        def test(self):
            # python是强类型语言,不支持字符串和数字直接相加
            return self.title+str(self.price)
    
    
    class Publish(models.Model):
        name=models.CharField(max_length=32)
        addr=models.CharField(max_length=32)
    
        def __str__(self):
            return self.name
    models.py
    from django.shortcuts import render
    
    # Create your views here.
    from rest_framework.views import APIView
    from rest_framework.response import Response
    from myModelSerializer import models
    # from app01.serializer import BookModelSerializer as BookSerializer  # 一种替换序列化器的技巧
    from myModelSerializer.serializer import BookModelSerializer
    from myModelSerializer.serializer import BookSerializer
    
    '''5个接口
    1 查询所有   Book--》get
    2 查询单个   BookDetali--》get
    3 新增一个   Book--》post
    4 删除一个   BookDetali--》delete
    5 修改一个   BookDetali--》put   向地址books/1发送put请求
    '''
    
    class Book(APIView):
        def get(self, request, *args, **kwargs):
            res = models.Book.objects.all()
            # 借助序列化器
            # 如果是多条,就是many=True
            # 如果是单个对象,就不写
            # ser = BookSerializer(instance=res, many=True)
            ser = BookModelSerializer(instance=res, many=True)
            print(type(ser))  # rest_framework.serializers.ListSerializer
            # 通过序列化器得到的字典
            # ser.data
            print(ser.data)
            return Response(ser.data)
    
        def post(self, request):
            # post提交的数据都在request.data 是个字典
            print(request.data)
            # ser = BookSerializer(data=request.data)
            ser = BookModelSerializer(data=request.data)
            if ser.is_valid():  # 校验数据是否合法
                ser.save()  # 保存到数据库中
                return Response(ser.data)
            else:
                # 没有校验通过的错误信息
                return Response(ser.errors)
    
    
    
    
    
    # ==============================================
    
    class BookDetail(APIView):
        def get(self, request, id):
            res = models.Book.objects.all().filter(id=id).first()
            # 单个,去掉many=True
            # 加many=True和不加,ser不是同一个对象
            ser = BookSerializer(instance=res)
            print(type(ser))  # app01.serializer.BookSerializer
            return Response(ser.data)
    
        def put(self, request, id):
            # 通过id取到对象
            res = {'code': 100, 'msg': ''}
            try:
                book = models.Book.objects.get(id=id)
                ser = BookSerializer(instance=book, data=request.data)
                ser.is_valid(raise_exception=True)
                ser.save()
                res['msg'] = '修改成功'
                res['result'] = ser.data
    
            except Exception as e:
                res['code'] = 101
                res['msg'] = str(e)
    
            return Response(res)
    
        def delete(self,request,id):
            response = {'code': 100, 'msg': '删除成功'}
            models.Book.objects.filter(id=id).delete()
            return Response(response)
    子应用-views.py
    # 序列化器类(序列化Book表)
    from rest_framework import serializers
    from rest_framework.exceptions import ValidationError
    from myModelSerializer import models
    
    # 一、Serializer序列化器
    # BookSerializer
    class BookSerializer(serializers.Serializer):
        id=serializers.IntegerField(required=False)
        name=serializers.CharField(max_length=32,min_length=2,source='title')
        price=serializers.DecimalField(max_digits=5,decimal_places=2)
        publish=serializers.CharField(max_length=32,source='publish.name')
        publish_addr=serializers.CharField(source='publish.addr')
        # xxx=serializers.CharField(source='test')
    
        def create(self, validated_data):
            res=models.Book.objects.create(**validated_data)
            return res
    
        def update(self, instance, validated_data):
            instance.title=validated_data.get('title')
            instance.price=validated_data.get('price')
            instance.publish=validated_data.get('publish')
            instance.save()
            return instance
    
    # 二、ModelSerializer 表模型序列化器
    # ModelSerializer 表模型序列化器,与模型表一一对应的序列化器;即专用于指定的表模型;一个表,可以有多个序列化器
    # 表模型序列化器,继承serializers.Serializer;新增/修改 不需要手动重写 反序列化的 create /update 方法,因为序列化器源码已经重写了 反序列化的 create /update 方法
    
    # 1 BookModelSerializer
    # version 1——序列化部分字段
    class BookModelSerializer(serializers.ModelSerializer):
        # 3-1 重写'publish'
        publish=serializers.CharField(max_length=32,source='publish.name')
        # 3-2 新增'addr'字段
        addr=serializers.CharField(source='publish.addr')
        #1、内部类 Meta
        class Meta:
            # 2、与哪张表建立关系,注意1 :变量model不加s,否则就与内置的models冲突了
            model=models.Book
            # 3、需要序列化哪些字段
            # fields=['id','title','price']  # 模型表中已有的字段
            # 注意2:fields 为复数
            fields=['id','title','price','publish','addr']  # 重写 'publish',让其在前端显示具体出版社信息;新增 'addr' 字段,需要在模型序列化器里定义该字段
    
    # version 2——序列化/反序列化 所有字段,支持改写已有字段,但是不支持增加新的序列化字段
    class BookModelSerializer(serializers.ModelSerializer):
        publish = serializers.CharField(max_length=32, source='publish.name')
        class Meta:
            model=models.Book
            fields='__all__'
    
    # version 3 —— 排除部分字段,不进行序列化
    class BookModelSerializer(serializers.ModelSerializer):
        class Meta:
            model=models.Book
            # fields='__all__'
            exclude=['publish','price'] # 排除列表指定的字段,注意 不能和 fields 同时使用
    
    
    # version 4 —— read_only_fields/write_only_fields/extra_kwargs
    class BookModelSerializer(serializers.ModelSerializer):
    
        class Meta:
            model=models.Book
            fields='__all__'
            read_only_fields=['price','publish']  # 只用于序列化输出
            write_only_fields=['title',]  # 只用于反序列化输入 / 可能有的drf版本不能使用(实际使用再看啦)
            # extra_kwargs:给字段额外添加参数——在序列化器里重写字段属性比较麻烦,因此可以使用extra_kwargs
            # extra_kwargs={'title':{'max_length':64},'price':{'validators':[check,]}}
            extra_kwargs={'title':{'max_length':64,'write_only':True},}  # 也可以使用这种方式来指定write_only/write_only
    
        # 局部钩子/全局钩子 跟 Serializers序列化器的一样写法。注意:不要错写在内置类Meta类中!!
    
    
    # 了解 # version 5 —— depth 连表查询返回结果
    class BookModelSerializer(serializers.ModelSerializer):
        # publish = serializers.CharField(max_length=32, source='publish.name')
        class Meta:
            model = models.Book
            fields = '__all__'
            depth=1  # 连表查询,将关联的publish表的数据一次全部获取,而不会只返回出版社的id;如果book关联两张表,则depth可以设为2;
                    # 个人建议depth最多不要超过3,尽量不要用
    
    
    # 2 ModelSerializer中 serializers.SerializerMethodField 的用法
    class BookModelSerializer(serializers.ModelSerializer):
        class Meta:
            model = models.Book
            fields = '__all__'
    
        publish = serializers.SerializerMethodField()
        def get_publish(self,obj):
            dic={'name':obj.publish.name,'addr':obj.publish.addr}
            return dic
    
    
    # 3 模型序列化器类的嵌套
    class PublishModelSerializer(serializers.ModelSerializer):
        class Meta:
            model=models.Publish
            # fields='__all__'
            fields=['name','addr']
    
    class BookModelSerializer(serializers.ModelSerializer):
        class Meta:
            model = models.Book
            fields = '__all__'
    
        # publish = serializers.SerializerMethodField()
        # def get_publish(self,obj):
        #     dic={'name':obj.publish.name,'addr':obj.publish.addr}
        #     return dic
        # 序列化类的嵌套——会按照嵌套的序列化类的规则,序列化目标字段
        publish = PublishModelSerializer()
    子应用-serializers.py

     

     

    参考:python/Django-rest-framework框架/2-drf-序列化组件

    关于定义序列化器时,read_only和write_only有什么作用

     

  • 相关阅读:
    iOS uiscrollView 嵌套 问题 的解决
    NSURLConnection 网络超时的那些事(转别人整理的)
    IOS 开发中判断NSString是否为空字符
    iOS- SQLite3的基本使用
    怎么应对 domino文档损坏然后损坏文档别删除导致数据丢失
    为什么Log.nsf中存储的日志只有最近7天的原因
    Struts,Spring,Hibernate优缺点
    Java面试之List的三个子类ArrayList,LinkedList,Vector区别
    Java面试之同步/异步/阻塞/非阻塞/BIO/NIO/AIO
    如何设置 Windows 开机启动项
  • 原文地址:https://www.cnblogs.com/bigorangecc/p/13924192.html
Copyright © 2011-2022 走看看