DRF 序列化与反序列化之 Serializer & ModelSerializer

  • 一、序列化:模型转换为JSON流程(只传 instance)
    • 1.1 创建数据模型
    • 1.2 新建serializers.py,简单序列化
    • 1.3 添加额外字段
    • 1.4 关联对象序列化
      • 1.4.1 获取对应外键字段主键
      • 1.4.2 获取外键关联的模型方法名称`__str__`方法
      • 1.4.3 获取外键关联模型的所有字段信息
      • 1.4.4 其它说明
  • 二、常用序列化字段及参数
  • 三、反序列化:JSON转换为模型流程(只要传了data)
    • 3.1 反序列化校验方法
      • 3.1.1 字段选项参数
      • 3.1.2 自定义方法校验
    • 3.2 保存数据
      • 3.2.1 新增数据 调用 create
      • 3.2.2 更新数据 调用 update
    • 3.3 附加说明
  • 四、模型序列化器类使用说明

一、序列化:模型转换为JSON流程(只传 instance)

  • 序列化的类应在该应用中单独创建一个serializers.py来编写
    1. 定义序列化器类(模型名/类视图名+Serializer),并继承自Serializer
    2. 定义序列化器中的字段应参照模型(models.py),序列化器中的字段名需要与模型一致,字段可以比模型字段多或少
    3. 对于有外键字段(OneToOneFieldManyToManyFieldForeignKey),在有外键字段里面关联序列化时用外键名,在没有外键字段里面关联序列化多时用模型名小写_set
    4. 在没有外键的一方关联序列化时需要指定关联字段many=true
    5. 将序列化模型对象或查询集(多个模型对象),传给序列化器类的instance参数,如果传的查询集需要在指定many=true
    6. 获取序列化后的数据,通过序列化器对象.data属性即可获取
    7. 序列化仅仅是模型转字典

1.1 创建数据模型

创建数据模型后,在后台添加点测试数据

from django.db import models

class Book(models.Model):
    title = models.CharField(max_length=32)
    created_time = models.DateField(auto_now_add=True)
    publish = models.ForeignKey('Publish',on_delete=models.CASCADE)
    class Meta:  # 模型元选项
        # db_table = 'tb_column'   # 在数据库中的表名,否则Django自动生成为app名字_类名
        # ordering = ['index']
        verbose_name = '书名'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.title

class Author(models.Model):
    name = models.CharField(max_length=20)
    book = models.ManyToManyField(Book)
    class Meta:  # 模型元选项
        # db_table = 'tb_column'   # 在数据库中的表名,否则Django自动生成为app名字_类名
        # ordering = ['index']
        verbose_name = '作者'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.name

class Publish(models.Model):
    publish = models.CharField(max_length=32)
    class Meta:  # 模型元选项
        # db_table = 'tb_column'   # 在数据库中的表名,否则Django自动生成为app名字_类名
        # ordering = ['index']
        verbose_name = '出版社'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.publish

class AuthorDetail(models.Model):
    address = models.CharField(max_length=32)
    author = models.OneToOneField(Author,on_delete=models.CASCADE)
    class Meta:  # 模型元选项
        # db_table = 'tb_column'   # 在数据库中的表名,否则Django自动生成为app名字_类名
        # ordering = ['index']
        verbose_name = '详情'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.address

1.2 新建serializers.py,简单序列化


from rest_framework import serializers

class BookSerializer(serializers.Serializer):
    title = serializers.CharField(max_length=32)
    created_time = serializers.DateField()
  • 序列化模型对象
python manage.py shell

In [3]: from book.serializers import BookSerializer

In [4]: from book.models import Book

In [5]: book = Book.objects.get(pk=1)

In [6]: ser = BookSerializer(book)

In [7]: ser.data
Out[7]: {'title': 'Python', 'created_time': '2021-10-08'}
  • 序列化查询集(多个模型对象,queryset对象)

In [1]: from book.serializers import BookSerializer

In [2]: from book.models import Book

In [3]: books = Book.objects.all()

In [4]: sers = BookSerializer(books,many=True)

In [5]: sers.data
Out[5]: [OrderedDict([('title', 'Python'), ('created_time', '2021-10-08')]), OrderedDict([('title', 'Vue'), ('created_time', '2021-10-08')])]

单获取的是多个对象需要指定many=True

1.3 添加额外字段

  • @property定义模型只读属性
# models.py 为 Book模型添加@property方法
class Book(models.Model):
    title = models.CharField(max_length=32)
    created_time = models.DateField(auto_now_add=True)
    publish = models.ForeignKey('Publish',on_delete=models.CASCADE)
    class Meta:  # 模型元选项
        # db_table = 'tb_column'   # 在数据库中的表名,否则Django自动生成为app名字_类名
        # ordering = ['index']
        verbose_name = '书名'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.title
    # @property是python的一种装饰器,是用来修饰方法的。
    # 我们可以使用@property装饰器来创建只读属性,@property装饰器会将方法转换为相同名称的只读属性
    # ,可以与所定义的属性配合使用,这样可以防止属性被修改。
    @property
    def ext(self):
        return f'出版社:name={self.publish.publish}'
# serializers.py 序列化额外字段
from rest_framework import serializers

class BookSerializer(serializers.Serializer):
    title = serializers.CharField(max_length=32)
    created_time = serializers.DateField()
    # 通过定义模型只读属性获取,这里应与@propert装饰器作用下的函数名名称一致
    ext = serializers.CharField(max_length=20)
In [1]: from book.serializers import BookSerializer

In [2]: from book.models import Book

In [3]: books = Book.objects.all()

In [4]: sers = BookSerializer(books,many=True)

In [5]: sers.data
Out[5]: [OrderedDict([('title', 'Python'), ('created_time', '2021-10-08'), ('ext', '出版社:name=CQJTU')]), OrderedDict([('title', 'Vue'), ('created_tim
e', '2021-10-08'), ('ext', '出版社:name=CQJTU')])]

book = Book.objects.get(pk=2)

In [7]: ser = BookSerializer(book)

In [8]: ser.data
Out[8]: {'title': 'Vue', 'created_time': '2021-10-08', 'ext': '出版社:name=CQJTU'}
  • 序列化器中自定义
from django.db import models

class Book(models.Model):
    title = models.CharField(max_length=32)
    created_time = models.DateField(auto_now_add=True)
    publish = models.ForeignKey('Publish',on_delete=models.CASCADE)
    class Meta:  # 模型元选项
        # db_table = 'tb_column'   # 在数据库中的表名,否则Django自动生成为app名字_类名
        # ordering = ['index']
        verbose_name = '书名'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.title
    # @property是python的一种装饰器,是用来修饰方法的。
    # 我们可以使用@property装饰器来创建只读属性,@property装饰器会将方法转换为相同名称的只读属性
    # ,可以与所定义的属性配合使用,这样可以防止属性被修改。
    @property
    def ext(self):
        return f'出版社:name={self.publish.publish}'

    @property
    def eee(self):
        return f'作者:{[i.name for i in self.author_set.all()]}'
from rest_framework import serializers

class BookSerializer(serializers.Serializer):
    title = serializers.CharField(max_length=32)
    created_time = serializers.DateField()
    # 通过定义模型只读属性获取
    ext = serializers.CharField(max_length=20)  # 名称对应相同则不需要指定source
    author = serializers.CharField(max_length=10,source="eee") # 不同时则需要指定source
    # SerializerMethodField 在此序列化类中get_xxx来重写
    ext_info = serializers.SerializerMethodField(label="额外字段")
    def get_ext_info(self,obj):
        return f'出版社:{obj.publish}'

In [1]: from book.serializers import BookSerializer

In [2]: from book.models import Book

In [3]: books = Book.objects.all()

In [4]: sers = BookSerializer(books,many=True)

In [5]: sers.data
Out[5]: [OrderedDict([('title', 'Python'), ('created_time', '2021-10-08'), ('ext', '出版社:name=CQJTU'), ('author', "作者:['AAA', 'asd']"), ('ext_info
', '出版社:CQJTU')]), OrderedDict([('title', 'Vue'), ('created_time', '2021-10-08'), ('ext', '出版社:name=CQJTU'), ('author', "作者:['ADSD']"), ('ext
_info', '出版社:CQJTU')])]

额外字段的新增方法主要有两种,一种是通过定义数据模型@property装饰器的方法来指定仅可读方法,这种方法中若定义时与额外字段相同则不需要指定source,反之;还有一种就是在序列化模型类中定义方法来修改,通过定义以get_额外字段名的函数名方法来进行改下。

1.4 关联对象序列化

模型关系分为一对一、多对一和多对多三种,当我们想从一个模型中获取相关联的模型的数据时,也可大致分为三种方式,获取关联模型的主键( 外键名_idPrimaryKeyRelatedField)、获取模型对应的__str__方法返回的值(StringRelatedField,用的较少)、还有就是获取关联模型的所有字段(将想要获取关联模型进行序列化后,然后在另外一个序列化类中利用显示出来即可),最后一种就是前面的额外字段的方法,但是有一定的局限性,有些情况不能很好的显示出来,但也可以实现。

1.4.1 获取对应外键字段主键

  • ① 通过 外键名_id方式获取对应的外键 id
# serializers.py
from rest_framework import serializers
class BookSerializer(serializers.Serializer):
    title = serializers.CharField(max_length=32)
    created_time = serializers.DateField()
    # 在 Django ORM 中,我们知道数据库中保存时为外键_id
    publish_id = serializers.IntegerField()
  • PrimaryKeyRelatedField,使用时需要添加read_only或者queryset
# serializers.py
class BookSerializer(serializers.Serializer):
    title = serializers.CharField(max_length=32)
    created_time = serializers.DateField()
    # 使用 PrimaryKeyRelatedField 应注意与模型里面的字段保持一致,
    # 并且通常需要设置 `get_queryset`, or set read_only=`True`,否则报错
    # publish = serializers.PrimaryKeyRelatedField(queryset=Publish.objects.all())
    publish = serializers.PrimaryKeyRelatedField(read_only=True)
In [1]: from book.serializers import BookSerializer

In [2]: from book.models import Book

In [3]: books = Book.objects.all()

In [4]: sers = BookSerializer(books,many=True)

In [5]: sers.data
Out[5]: [OrderedDict([('title', 'Python'), ('created_time', '2021-10-08'), ('publish', 1)]), OrderedDict([('title', 'Vue'), ('created_time', '2021-10-08
'), ('publish', 1)])]

1.4.2 获取外键关联的模型方法名称__str__方法

# serializers.py
... 同上
# 获取出版社__str__方法返回的信息
publish = serializers.StringRelatedField()
In [1]: from book.serializers import BookSerializer

In [2]: from book.models import Book

In [3]: books = Book.objects.all()

In [4]: sers = BookSerializer(books,many=True)

In [5]: sers.data
Out[5]: [OrderedDict([('title', 'Python'), ('created_time', '2021-10-08'), ('publish', 'CQJTU')]), OrderedDict([('title', 'Vue'), ('created_time', '2021
-10-08'), ('publish', 'CQJTU')])]

1.4.3 获取外键关联模型的所有字段信息

  • step1:定义被获取关联模型的序列化字段信息
  • step2:在需要获取关联模型的序列劣化字段你信息,并用外键名来实例化step1序列化的模型
  • 注意:必须是这个顺序,需要先有才能实例化
class PublishSerializer(serializers.Serializer):
    publish = serializers.CharField(max_length=32)

class BookSerializer(serializers.Serializer):
    title = serializers.CharField(max_length=32)
    created_time = serializers.DateField()
    # 使用 PrimaryKeyRelatedField 应注意与模型里面的字段保持一致,
    # 并且通常需要设置 `get_queryset`, or set read_only=`True`,否则报错
    # publish = serializers.PrimaryKeyRelatedField(queryset=Publish.objects.all())
    # publish = serializers.PrimaryKeyRelatedField(read_only=True)
    # 获取出版社__str__方法返回的信息
    publish = PublishSerializer()

1.4.4 其它说明

  • 对于正向查询:即有外键查无外键(三种关系)

Django ORM模型中学到,外键在数据库中保存的名自动加了_id,因此正向查询时就可以通过此来获取外键的主键了。通常情况下使用外键关联时,无须反序列化可以都设置为read_only=True。在多对多时需要指定many=True,同查询集。

  • 对于反向查询:即无外键查询有外键的模型(三种关系同)

反向查询可以通过模型名小写_set来获取相对应得查询集,这里除了一对一关系不需要指定many=True,其余都需要指定many=True一对一不需要加_set哦,小写表名就可以啦。

二、常用序列化字段及参数

官方字段及参数详情地址
选项参数

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

通用参数

参数名称 说明
read_only 表明该字段仅用于序列化输出,默认False
write_only 表明该字段仅用于反序列化输入,默认False
required 表明该字段是否必须传,在反序列化时必须输入,默认True。
default 反序列化时使用的默认值
allow_null 表明该字段是否允许传入None,默认False
validators 该字段使用的验证器
error_messages 包含错误编号与错误信息的字典
label 用于HTML展示API页面时,显示的字段名称
help_text 用于HTML展示API页面时,显示的字段帮助提示信息

三、反序列化:JSON转换为模型流程(只要传了data)

反序列化:从前端获取数据、序列化器的data、调用序列化器的is_valid()方法来叫校验、调用序列化器的save()方法、最后再进行序列化

  1. 获取前端传入的json字典数据
  2. 创建序列化器,给序列化器的data参数传参(需要以关键字参数进行传参)
  3. 调用序列化器的is_valid(raise_excption=True)进行校验,若校验出错自动抛出错误信息
  4. 调用序列化器的save()方法,调用save时会判断当初始序列化器中是否传入了instance
  5. 若 传入了instance也传入了data,那么调用save方法实际调用的就是序列化器中的update方法,若只有data则是调用序列化器中的create方法
  6. 反序列化最后会自动完成序列化,也是通过ser.data获取

3.1 反序列化校验方法

3.1.1 字段选项参数

在序列化中通过设置参数,如长度等

title = serializers.CharField(max_length=32)

3.1.2 自定义方法校验

自定义方法有三种形式,可以组合起来使用:

  • validate(联合校验,全部字段自定义校验,定义的方法函数名,位于序列化类中)
  • validate_字段名(局部校验,校验该字段名的数据,定义的方法函数名,位于序列化类中,一般就用联合校验就顺便把局部校验做了)
  • validators(字段参数,定义的方法函数名可以任意,位于序列化类外,使用方法通过在字段中validators=[method1,method2])
from rest_framework import serializers

def about_publish(value):
    if 'django' not in value.lower():
        raise serializers.ValidationError("未含有Django!")
    return value
class PublishSerializer(serializers.Serializer):
    publish = serializers.CharField(max_length=32,validators=[about_publish])
    datetime = serializers.SerializerMethodField(read_only=True)
    def get_datetime(self,obj):
        # 额外参数
        import datetime
        return f'{datetime.datetime.now()}'
    def validate(self, attrs):
        if '出版社' not in attrs['publish'].lower():
            raise serializers.ValidationError("未含有出版社!")
        return attrs

3.2 保存数据

验证数据成功后,用序列化器来完成数据反序列化,把数据转成模型类对象,通过实现create()update()两个方法来实现。如果创建序列化器对象的时候,没有传递instance实例,则调用save()方法的时候,create()被调用;如果传递了instance实例,则调用save()方法的时候,update()被调用。

from rest_framework import serializers

def about_publish(value):
    if 'django' not in value.lower():
        raise serializers.ValidationError("未含有Django!")
    return value
class PublishSerializer(serializers.Serializer):
    publish = serializers.CharField(max_length=32,validators=[about_publish])
    datetime = serializers.SerializerMethodField(read_only=True)
    def get_datetime(self,obj):
        # 额外参数
        import datetime
        return f'{datetime.datetime.now()}'
    def validate(self, attrs):
        if '出版社' not in attrs['publish'].lower():
            raise serializers.ValidationError("未含有出版社!")
        return attrs
    def create(self, validated_data):
        # 新增数据
        publish = Publish.objects.create(**validated_data)
        return publish
    def update(self, instance, validated_data):
        # 更新数据,这里的instance就是传入的install = 模型.objects.get(id=1) (obj模型对象)
        instance.publish = validated_data["publish"]
        instance.save()
        return instance

3.2.1 新增数据 调用 create

In [11]: from book.serializers import PublishSerializer

In [12]: from book.models import Publish

In [13]: data = {
    ...: "publish":"Django出版社DRF"
    ...: }

In [14]: ser = PublishSerializer(data=data)

In [15]: ser.is_valid(raise_exception=True)
Out[15]: True

In [16]: ser.save()
Out[16]: <Publish: Django出版社DRF>

In [17]: ser.data
Out[17]: {'publish': 'Django出版社DRF', 'datetime': '2021-10-09 10:03:09.150345'}

In [18]: [i.publish for i in Publish.objects.all())
  File "", line 1
    [i.publish for i in Publish.objects.all())
                                             ^
SyntaxError: closing parenthesis ')' does not match opening parenthesis '['

# 可以看到新增成功
In [19]: [i.publish for i in Publish.objects.all()]
Out[19]: ['CQJTU', 'Djangosa出版社', 'Djangosa出版社', 'Django出版社DRF']

3.2.2 更新数据 调用 update

In [1]: from book.models import Publish
# 修改前
In [2]: [i.publish for i in Publish.objects.all()]
Out[2]: ['CQJTU', 'Djangosa出版社', 'Djangosa出版社', 'Django出版社DRF']
# 指定修改哪一个
In [3]: pub = Publish.objects.get(pk=1)

In [4]: from book.serializers import PublishSerializer

In [5]: ser = PublishSerializer(instance=pub,data={'publish':'CQJTU出版社Django'})

In [6]: ser.is_valid()
Out[6]: True

In [7]: ser.save()
Out[7]: <Publish: CQJTU出版社Django>

In [8]: ser.data
Out[8]: {'publish': 'CQJTU出版社Django', 'datetime': '2021-10-09 10:10:28.303865'}
# 修改后
In [9]:  [i.publish for i in Publish.objects.all()]
Out[9]: ['CQJTU出版社Django', 'Djangosa出版社', 'Djangosa出版社', 'Django出版社DRF']

3.3 附加说明

  • 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)

四、模型序列化器类使用说明

  • ModelSerializer继承自Serializer
  • 它可以根据模型来自动生成序列化器中的字段
  • 它里面实现了createupdate方法
  • 但是我们也可以如通用Serializer来重写一些自定义方法来校验
class BookModelSerializer(serializers.ModelSerializer):
    ext = serializers.CharField()
    class Meta:
        ''' 指定需要序列化模型'''
        model = Book
        ''' 指定模型中需要序列化的字段
        新增字段需要加上在后面加上
        '''
        # 模型中所有字段
        # fields = '__all__'
        # 模型中部分字段
        # fields = ['title','created_time','ext']
        ''' 排除模型中需要序列化的字段
        新增字段不需要加上
        '''
        # 除了 title 不需要其他全都需要(包括新增字段)
        exclude = ['title',]
        '''修改字段里面的参数'''
        # 当生成自动的参数不满足需求时,当然也可以自定方法来进行校验,方法同 Serializer
        # 此处不可修改 read_only = True
        extra_kwargs = {
            'title':{
                'write_only':True,
                'max_length':32,
            },
            'created_time':{
                'format':"%Y-%m-%d %H:%M:%S"
            }
        }
        '''指定哪些字段只做序列化'''
        read_only_fields = ["created_time"]
    # 额外参数
    def get_ext(self,obj):
        return f'其他参数:{obj.title}'
    # 自定义方法校验
    def validate(self, attrs):
        if 'django' not in attrs["title"].lower():
            raise serializers.ValidationError("书名没有包含有Django")
        return attrs

Serializer不仅仅是可用于校验数据存储,还可以用于存于无数据模型中如redis等的校验,而ModelSerializer仅适用于有数据模型时使用。

你可能感兴趣的:(Django,django,python)