序列化类常用字段和参数,序列化高级用法之定制字段的方式,多表关联之反序列化,反序列化字段校验

 序列化类的字段类型和参数

常用字段类型

字段字段构造方式
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=)

字段参数

  • 针对 CharField 类型
参数作用
max_length 最大长度
min_lenght 最小长度
allow_blank 是否允许为空
  • 针对 InterField 类型
参数作用
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页面时,显示的字段帮助提示信息

序列化高级用法之source(了解)

数据准备

# models.py

from django.db import models

# Create your models here.
class Book(models.Model):
    name = models.CharField(max_length=32)
    price = models.CharField(max_length=32)

    publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE)
    authors = models.ManyToManyField(to='Author')


class Publish(models.Model):
    name = models.CharField(max_length=32)
    addr = models.CharField(max_length=32)


class Author(models.Model):
    name = models.CharField(max_length=32)
    phone = models.CharField(max_length=11)

模型表创建好之后进行数据库迁移,录入数据。

序列化定制字段名字

# serializer.py

from rest_framework import serializers


class BookSerializer(serializers.Serializer):
    # 将字段名name修改为real_name,通过source指定原来的字段名
    real_name = serializers.CharField(max_length=32,source='name')
    price = serializers.CharField()

    # 一对多外键字段,通过source可以指定外键字段关联的数据(通过外键用的的方式取值)
    publish = serializers.CharField(source='publish.name')

    # 多对多,source不能用
    authors = serializers.CharField(source='authors.name')

执行结果如下:

序列化高级用法之定制字段的两种方式

要想把数据以下面这种方式返回给前端:

'''
 {
    "name": "西游记",
    "price": "55",
    "publish":{"name":"北京出版社","addr":"北京"}, # 真实显示出版社表中的所有数据
    authors:[{"name":"xxx","phone":"1111"},{"name":"yyy","phone":"12345……"}]   # 真实显示本书的所有作者表中的信息 
}
'''

SerializerMethodField定制

# 定制关联字段的显示形式
    -一对多的,显示字典
    -多对多,显示列表套字典

# serializer.py

from rest_framework import serializers


class BookSerializer(serializers.Serializer):
    # 将字段名name修改为real_name,通过source指定原来的字段名
    name = serializers.CharField(max_length=32)
    price = serializers.CharField()

    publish = serializers.SerializerMethodField()
    # get_后面的字段必须与序列化的字段名一致,即一定得是publish
    def get_publish(self, obj):  # obj指的是当前序列化的对象,即book对象
        return {'name': obj.publish.name, 'addr': obj.publish.addr} # 可以自己指定要返回的数据

    # 多对多
    author_list = serializers.SerializerMethodField()
    def get_author_list(self, obj):  # obj指的是当前序列化的对象,即book对象
        l = []for author in obj.authors.all():
            l.append({'name': author.name, 'phone': author.phone})
        return l

执行结果如下:

 

在表模型中定制

# models.py

from django.db import models


class Book(models.Model):
    name = models.CharField(max_length=32)
    price = models.CharField(max_length=32)

    publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE)
    authors = models.ManyToManyField(to='Author')

    @property
    def publish_detail(self):  # self指的是当前book对象
        return {'name': self.publish.name, 'addr': self.publish.addr} # 可以自己指定要返回的数据

    @property
    def author_list(self):  # self指的是当前book对象
        l = []

        for author in self.authors.all():
            l.append({'name': author.name, 'phone': author.phone})
        return l

# serializer.py

from rest_framework import serializers


class BookSerializer(serializers.Serializer):

    name = serializers.CharField(max_length=32)
    price = serializers.CharField()

    publish_detail = serializers.DictField()
    author_list = serializers.ListField()

图解:

 

 

read_only 和 write_only

  • read_only:默认False,设置read_only=True:表明该字段仅用于序列化输出(只能查看,不能修改)
  • write_only:默认False,设置write_only=True:表明该字段仅用于反序列化输入(只能修改,不能查看)
    # name和price,即用来序列化,又用来反序列化,既写又读
    name = serializers.CharField(max_length=32)
    price = serializers.CharField()

    # 只用来做序列化,只读, read_only
    publish_detail = serializers.DictField(read_only=True)
    author_list = serializers.ListField(read_only=True)

    # 只用来做反序列化,只写,write_only
    publish = serializers.CharField(write_only=True)
    authors = serializers.ListField(write_only=True)

 注意:在表的操作中,一般都是既要做序列化,又要做反序列化,一定要指明哪些字段是read_only,哪些字段是write_only。

多表关联反序列化保存

新增图书接口

 首先要明确一点,前端传入的数据,绑定了外键字段的,是根据绑定的id值进行选择相应的数据传入,而不是直接输入一条数据。类似于下面的格式:

{"name""西游记", "price""55", "publish": 1,  "authors": [1,2]}

# views.py

from rest_framework.views import APIView
from .serializer import BookSerializer
from rest_framework.response import Response

class BookView(APIView):

    def post(self,request):
        ser = BookSerializer(data=request.data)
        if ser.is_valid():
            ser.save()
            return Response({'code':100,'msg':'新增成功'})
        else:
            return Response({'code':101,'msg':ser.errors})

# serializer.py

from rest_framework import serializers
from .models import Book


class BookSerializer(serializers.Serializer):
    # name和price,即用来序列化,又用来反序列化,既写又读
    name = serializers.CharField(max_length=32)
    price = serializers.CharField()

   
    # 只用来做反序列化,只写,write_only
    publish = serializers.CharField(write_only=True)
    authors = serializers.ListField(write_only=True)


    # 新增要重写create方法
    def create(self, validated_data):
    # validated_data 校验过后的数据,{"name": "西游记", "price": "55", "publish": 1,  "authors": [1,2]}
        # 新增一本图书
        book = Book.objects.create(name=validated_data.get('name'),price=validated_data.get('price'),
                                   publish_id=validated_data.get('publish'))

        # 作者表也要关联上
        book.authors.add(*validated_data.get('authors'))  # 将列表打散,相当于book.authors.add(1,2)
        return book

修改图书接口

# urls.py

path('books/<int:pk>/',views.BookDetailView.as_view())

# views.py

class BookDetailView(APIView):
    def put(self,request,pk):
        book = Book.objects.filter(pk=pk).first()
        ser = BookSerializer(data=request.data,instance=book)
        if ser.is_valid():
            ser.save()
            return Response({'code':100,'msg':'修改成功'})
        else:
            return Response({'code':101,'msg':ser.errors})

# serializer.py

from rest_framework import serializers
from .models import Book


class BookSerializer(serializers.Serializer):
    # name和price,即用来序列化,又用来反序列化,既写又读
    name = serializers.CharField(max_length=32)
    price = serializers.CharField()


    # 只用来做反序列化,只写,write_only
    publish = serializers.CharField(write_only=True)
    authors = serializers.ListField(write_only=True)


    # 修改要重写update
    def update(self, instance, validated_data):
    # validated_data 校验过后的数据,{"name": "西游记", "price": "55", "publish": 1,  "authors": [1,2]}
        instance.name = validated_data.get('name')
        instance.price = validated_data.get('price')
        instance.publish_id = validated_data.get('publish')  # 注意数据库中外键字段名会自动加_id

        authors = validated_data.get('authors')
        # 方法1:先清空,再add
        # instance.authors.clear()
        # instance.authors.add(*authors)
     # 方法2:也可以直接用set instance.authors.set(authors) instance.save() return instance

 

反序列化字段校验

总共有4层校验

1. 序列化内字段自带的校验

name = serializers.CharField(max_length=8, error_messages={'max_length': '最长只有为8位'})

2. 使用字段参数 validators 来进行校验

  • 该参数可以指定校验函数, 适用于多个字段都要进行同一种校验
  • 如果每个都写钩子就很麻烦, 于是就可以写一个校验函数, 每个字段都使用 validators 参数来指定该函数
# 定义一个校验函数
def name_start(data):
    if data.startswith('xxx'):
        raise ValidationError('不能以"xxx"开头')
    else:
        return data
        
# 然后在需要进行该校验的字段中加入 validators 参数指定该校验函数
author = serializers.CharField(validators=[name_start])
...
city = serializers.CharField(validators=[name_start])
...

3. 局部钩子

validate_[字段名]

# 校验名字不能以"xxx"开头

def validate_name(self, data):
    # data就是当前字段的值
    if data.startswith('xxx'):
           raise ValidationError('不能以"xxx"开头')
    else:
        return data  # 返回校验后的值

4. 全局钩子validate

全局钩子:
# 校验名字不能与所在城市同名
def validate(self, attrs): 
    name = attrs.get('name')
    city = attrs.get('city')
    if name == city:
        raise ValidationError('名字不能和城市名一样!')
    else:
        return attrs  # 返回所有数据

ModelSerializer使用

  • 上面我们使用的Serializer 序列化类,它不是只能为数据库模型类定义, 也可以为非数据库模型类的数据定义 (也就是自己定义的数据进行序列化 : {“name”:“xxx”,“age”:18}),
  • serializer是独立于数据库之外的存在, 需要自己在序列化类中去指定模型类的字段
  • 而如果我们想要使用序列化器对应的是Django的模型类, DRF为我们提供了ModelSerializer模型类序列化器来帮助我们快速创建一个Serializer类

ModelSerializer相比较于Serializer提供了一些功能

  • 基于模型类自动生成一系列字段 (也就是不再需要自己手动定义字段)
  • 基于模型类自动为 Serializer 生成 validators,比如 : unique_together
  • 包含默认的create( )update( )的实现, 不再需要重写这两种方法

模型类序列化器的定义

  • model:指明参照哪个模型类
  • fields:指明为模型类的哪些字段生成
  • extra_kwargs : 使用该参数为ModelSerializer添加或修改原有的选项参数 (重点, 更好的方法)

代码示例:

class BookSerializer(serializers.ModelSerializer):
    class Meta:
        model = Book  # 序列化类与表模型Book类建立了关联
        # fields='__all__'    # 序列化所有Book中的字段
        fields = ['name', 'price', 'publish', 'authors', 'publish_detail', 'author_list']  # 新增的字段要在这个表中进行注册

        # 定制name反序列化时,最长不能超过8   给字段类加属性---方式一
        extra_kwargs = {'name': {'max_length': 8},
                        'publish_detail': {'read_only': True},
                        'author_list': {'read_only': True},
                        # publish_detail和author_list是新增的两个字段,只用来序列化,设置read_only
                        'publish': {'write_only': True},
                        'authors': {'write_only': True},
                        # publish和author只用来反序列化,设置write_only
                        }

    # 如果Meta写了__all__ ,就相当于,复制了表模型中的所有字段,放在了这里,做了个映射,由此可有得到方式二
    # name = serializers.CharField(max_length=32)
    # price = serializers.CharField(max_length=32)

    # 定制name反序列化时,最长不能超过8   给字段类加属性---方式二,重写name字段
    name = serializers.CharField(max_length=8)

    # 添加两个新的字段(也可以在表模型中定制)
    publish_detail = serializers.SerializerMethodField(read_only=True)
    # get_后面的字段必须与上面的一致,即一定得是publish
    def get_publish_detail(self, obj):  # obj指的是当前序列化的对象,即book对象
        return {'name': obj.publish.name, 'addr': obj.publish.addr}  

    author_list = serializers.SerializerMethodField(read_only=True)
    def get_author_list(self, obj):  # obj指的是当前序列化的对象,即book对象
        l = []
        print(obj)
        for author in obj.authors.all():
            l.append({'name': author.name, 'phone': author.phone})
            print(l)
        return l

    # 局部钩子和全局钩子跟之前完全一样
    def validate_name(self, name):
        if name.startswith('sb'):
            raise ValidationError('不能以sb开头')

        else:
            return name

 

posted @ 2023-02-02 23:10  莫~慌  阅读(223)  评论(0编辑  收藏  举报