drf——序列化组件

drf——序列化组件

目录

  • 序列化组件的介绍
  • 序列化组件基本使用(查询所有,查询单个)
  • 序列化类常用的字段类和字段属性
  • 序列化类的高级用法
  • 反序列化——数据校验
  • 模型类序列化器(ModelSerializer)的使用
  • 断言——assert

序列化组件的介绍

序列化组件是什么

​ 序列化组件就是 drf 提供的一个类,我们要继承他,写自己的类

序列化组件的作用

​ 序列化组件的作用就是用来 序列化qs或者单个对象的

如何实施序列化组件

  • ①先获取所有的相关接口

  • ②将qs或者单个book对象转成json格式的字符串

  • ③ 将json格式的字符串传给前端 ——序列化

    ​ 使用for循环,用列表套字典的形式拼接

drf提供了一种可以快速实现序列化的类:序列化类

序列化组件的基本使用

定义一个序列化类

写序列化类, 给表数据进行序列化

​ 调用方法 from rest_framework.serializers import Serializer

from rest_framework import serializers

class BookSerializer(serializers.Serializer):
    # 要序列化的字段    有很多字段类,字段类有很多字段属性
    name = serializers.CharField()  # 字段类
    # price = serializers.CharField()
    publish = serializers.CharField()

使用序列化类,序列化多条数据

class BookView(APIView): # APIView继承自django的View   
    def get(self, request):
        book_list = Book.objects.all()
        # instance表示要序列化的数据,many=True表示序列化多条(instance是qs对象,一定要传many=True)
        ser = BookSerializer(instance=book_list, many=True)

        return Response(ser.data)

使用序列化类,序列化单条数据

class BookDetailView(APIView):
    def get(self, request, pk):
        book = Book.objects.filter(pk=pk).first()
        ser = BookSerializer(instance=book)
        return Response(ser.data)

使用反序列化

使用反序列化新增、修改数据。需要前端传入数据,经过校验后,添加或修改数据,其中,序列化类中有数据校验功能,需要重写方法,否则新增或修改失败

新增数据

  • 视图类

    class BookView(APIView):  # APIView继承自django的View
        def post(self, request):
            # 前端传递数据,从request.data取出来
            ser = BookSerializer(data=request.data)
            if ser.is_valid():  # 表示校验前端传入的数据   没有写校验规则,现在等于没校验
                ser.save()  # 再写东西,这里会报错  调用save会触发BookSerializer的save方法,判断了,如果instance有值执行update,没有值执行create
                return Response(ser.data)
            else:
                return Response(ser.errors)
    
  • 序列化类

    class BookSerializer(serializers.Serializer):
        # 要序列化的字段    有很多字段类,字段类有很多字段属性
        name = serializers.CharField()  # 字段类
        price = serializers.CharField()
        publish = serializers.CharField()
    
        # 重写create方法,
        def create(self, validated_data):
            res = Book.objects.create(**validated_data)
            return res
    

修改数据

  • 视图类

    class BookDetailView(APIView):
        def put(self, request, pk):
            book = Book.objects.filter(pk=pk).first()
            # 前端传递数据,从request.data取出来
            ser = BookSerializer(instance=book, data=request.data)
            if ser.is_valid():  # 表示校验前端传入的数据   没有写校验规则,现在等于没校验
                ser.save()  # 再写东西,这里会报错  调用save会触发BookSerializer的save方法,判断了,如果instance有值执行update,没有值执行create
                return Response(ser.data)
            else:
                return Response(ser.errors)
    
  • 序列化类

    class BookSerializer(serializers.Serializer):
        # 要序列化的字段    有很多字段类,字段类有很多字段属性
        name = serializers.CharField()  # 字段类
        price = serializers.CharField()
        publish = serializers.CharField()
    
        # 重写update
        def update(self, instance, validated_data):
            # instance要修改的对象
            # validated_data 校验过后的数据
            instance.name = validated_data.get('name')
            instance.price = validated_data.get('price')
            instance.publish = validated_data.get('publish')
            instance.save()
            return instance
    

序列化类常用字段类和字段参数

常用字段类

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

常用的字段为:

CharField
BooleanField
IntegerField
DecimalField

常用字段参数

  • 给CharField字段类使用的参数

    参数名 作用
    max_length 最大长度
    min_lenght 最小长度
    allow_blank 是否允许为空
    trim_whitespace 是否截断空白字符
  • 给IntegerField字段类使用的参数

    参数名 作用
    max_value 最小值
    min_value 最大值
  • 通用参数:放在哪个字段类上都可以的

    参数名 作用
    required 表明该字段在反序列化时必须输入,默认True
    default 反序列化时使用的默认值
    allow_null 表明该字段是否允许传入None,默认False
    validators 该字段使用的验证器【不需要了解】
    error_messages 包含错误编号与错误信息的字典
    label 用于HTML展示API页面时,显示的字段名称
    help_text 用于HTML展示API页面时,显示的字段帮助提示信息
  • 重要的两个参数字段

    参数名 作用
    read_only 表明该字段仅用于序列化输出,默认False
    write_only 表明该字段仅用于反序列化输入,默认False

序列化类的高级用法

source用法

source的用法就是可以将 表中的字段名在前端显示的时候重命名为其他字段名

  • 用法

    book_name = serializers.CharField(max_length=8, min_length=3,source='name')
    

    使用source,字段参数,可以指定序列化表中得哪个字段

  • 作用

    ①source指定的可以是字段,也可以是方法,用于重命名

    ②source可以做跨表查询

  • 代码演示

    class BookSerializer(serializers.Serializer):
        name_detail = serializers.CharField(max_length=8, min_length=3,source='name')
        # 或
        publish_name = serializers.CharField(max_length=8, min_length=3,source='publish.name')
        # 或
        xx = serializers.CharField(max_length=8, min_length=3,source='xx') #source的xx表示表模型中得方法
    

定制序列化字段

在【序列化类】中写SerializerMethodField

publish = serializers.SerializerMethodField()
def get_publish(self, obj):
    # obj 是当前序列化的对象
    return {'name': obj.publish.name, 'city': obj.publish.city, 'email': obj.publish.email}

使用SerializerMethodField演示代码

SerializerMethodField必须配合一个方法(get_字段名,需要接受一个参数),方法返回什么,这个字段就是什么

  • 创建序列化类 serializer.py
class BookSerializer(serializers.Serializer):
    name = serializers.CharField(max_length=8, min_length=3)
    price = serializers.IntegerField(min_value=10, max_value=99)
    publish_date = serializers.DateField()
    # 要将publish序列化成 {name:北京出版社,city:北京,email:2@qq.com}的形式
    
    publish = serializers.SerializerMethodField()
    def get_publish(self, obj):
        # obj 是当前序列化的对象
        return {'name': obj.publish.name, 'city': obj.publish.city, 'email': obj.publish.email}
  • 拓展,用此方式显示所有的作者对象

    class BookSerializer(serializers.Serializer):
    	authors = serializers.SerializerMethodField()
        def get_authors(self, obj):
            res_list = []
            for author in obj.authors.all():
                res_list.append({'id': author.id, 'name': author.name, 'age': author.age})
                return res_list
    

在【表模型】中写方法

def publish_detail(self):
    return {'name': self.publish.name, 'city': self.publish.city, 'email': self.publish.email}
在序列化中取
publish_detail=serializers.DictField()

在模型类中写逻辑代码,称之为ddd,领域驱动模型

  • 在models.py中(表模型)

    class Book(models.Model):
        name = models.CharField(max_length=32)
        price = models.DecimalField(max_digits=5, decimal_places=2)
        publish_date = models.DateField(null=True)
    
        publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE)
        authors = models.ManyToManyField(to='Author')
    
        # 写了个方法,可以包装成数据属性,也可以不包
        def publish_detail(self):
            return {'name': self.publish.name, 'city': self.publish.city, 'email': self.publish.email}
    
        def author_list(self):
            res_list = []
            for author in self.authors.all():
                res_list.append({'id': author.id, 'name': author.name, 'age': author.age})
            return res_list
    
  • 在serializer.py中(序列化类)

    class BookSerializer(serializers.Serializer):
        name = serializers.CharField(max_length=8, min_length=3)
        price = serializers.IntegerField(min_value=10, max_value=99)
        publish_date = serializers.DateField()
    
        # 方式二:在表模型中写方法
        publish_detail = serializers.DictField(read_only=True)
        # 练习,使用方式二实现,显示所有作者
        author_list = serializers.ListField(read_only=True)
    

有关联关系表的反序列化的保存

  • 序列化字段和反序列化字段不一样

    在serializer.py中(序列化类)

    # 反序列化用的
    publish = serializers.CharField(write_only=True)
    authors = serializers.ListField(write_only=True)
    #序列化用的
    publish_detail = serializers.DictField(read_only=True)
    author_list = serializers.ListField(read_only=True)
    
  • 一定要重写create

    在serializer.py中(序列化类)

    def create(self, validated_data): 
        # validated_data 校验过后的数据  
        {"name":"三国1演义",
         "price":19,
         "publish_date": "2022-09-27",
         "publish":1,
         "authors":[1,2]
        }
        book = Book.objects.create(name=validated_data.get('name'),
                                   price=validated_data.get('price'),
                                   publish_date=validated_data.get('publish_date'),
                                   publish_id=validated_data.get('publish'),
                                  )
        authors = validated_data.get('authors')
        book.authors.add(*authors)
        return book
    

    使用继承Serializer的序列化类保存需要重写create方法

    其缺点为:

    ①在序列化中每个字段都要写,无论是序列化还是反序列化

    ②如果新增或者修改,在序列化类中都需要重写create或update

    为了解决这个缺点,所以大多数情况下都是使用ModelSerializer来做

反序列化——数据校验

反序列化的数据校验与forms数据校验很像

反序列化的数据校验的顺序

①先校验字段自己特定的校验规则

②自己特定的校验规则通过后,校验局部钩子

③最后校验全局钩子

字段自己的校验规则的设定

  • 如果继承的是Serializer
name=serializers.CharField(max_length=8,min_length=3,error_messages={'min_length': "太短了"})
  • 如果继承的是ModelSerializer

    extra_kwargs = {
        'name': {'max_length': 8, 'min_length': 3, 'error_messages': {'min_length': "太短了"}},
    }
    

局部钩子

继承Serializer与继承ModelSerializer的写法一样

def validate_name(self, name):
    if name.startswith('sb'):
        # 校验不通过,抛异常
        raise ValidationError('不能以sb卡头')
        else:
            return name

全局钩子

继承Serializer与继承ModelSerializer的写法一样

def validate(self, attrs):
    if attrs.get('name') == attrs.get('publish_date'):
        raise ValidationError('名字不能等于日期')
        else:
            return attrs

模型类序列化器(ModelSerializer)的使用

class BookModelSerializer(serializers.ModelSerializer): #ModelSerializer继承Serializer
    # 不需要写字段了,字段从表模型映射过来
    class Meta:
        model = Book  # 要序列化的表模型
        # fields='__all__' # 所有字段都序列化
        fields = ['name', 'price', 'publish_date', 'publish', 'authors', 'publish_detail',
                  'author_list']  # 列表中有什么,就是序列化哪个字段
         # 给authors和publish加write_only属性
        # name加max_len属性
        extra_kwargs = {
            'name': {'max_length': 8},
            'publish': {'write_only': True},
            'authors': {'write_only': True},
        }  
   publish_detail = serializers.SerializerMethodField(read_only=True)
	 ...
   author_list = serializers.SerializerMethodField(read_only=True)
	...
  • 总结

    ①定义一个类继承ModelSerializer

    ②类内部写内部内 class Meta:

    ③ 在内部类中指定model(要序列化的表)

    ④在内部类中指定fields(要序列化的字段,写__all__表示所有,不包含方法,写[一个个字段])

    ⑤在内部类中指定extra_kwargs,给字段添加字段参数的

    ⑥在序列化类中,可以重写某个字段,优先使用你重写的

      name = serializers.SerializerMethodField()
        	def get_name(self, obj):
            	return 'sb---' + obj.name
    

    ⑦以后不需要重写create和update了

    ​ 因为ModelSerializer写好了,兼容性更好,任意表都可以直接存

反序列化数据校验源码分析(了解)

# 先校验字段自己的规则(最大,最小),走局部钩子校验,走全局钩子
# 局部:validate_name,全局叫:validate 为什么?

# 入口:从哪开始看,哪个操作,执行了字段校验ser.is_valid()
	-BaseSerializer内的is_valid()方法
	    def is_valid(self, *, raise_exception=False):
            if not hasattr(self, '_validated_data'):
                try:
                    # 真正的走校验,如果成功,返回校验过后的数据
                    self._validated_data = self.run_validation(self.initial_data)
                except ValidationError as exc:
            return not bool(self._errors)
    
    -内部执行了:self.run_validation(self.initial_data)---》本质执行的Serializer的
    	-如果你按住ctrl键,鼠标点击,会从当前类中找run_validation,找不到会去父类找
    	-这不是代码的执行,代码执行要从头开始找,从自己身上再往上找
    	    def run_validation(self, data=empty):
                #局部钩子的执行
                value = self.to_internal_value(data)
                try:
                    # 全局钩子的执行,从根上开始找着执行,优先执行自己定义的序列化类中得全局钩子
                    value = self.validate(value)
                except (ValidationError, DjangoValidationError) as exc:
                    raise ValidationError(detail=as_serializer_error(exc))

                return value
  -全局钩子看完了,局部钩子---》 self.to_internal_value---》从根上找----》本质执行的Serializer的
     def to_internal_value(self, data):
        for field in fields: # fields:序列化类中所有的字段,for循环每次取一个字段对象
            # 反射:去self:序列化类的对象中,反射 validate_字段名 的方法
            validate_method = getattr(self, 'validate_' + field.field_name, None)
            try:
                # 这句话是字段自己的校验规则(最大最小长度)
                validated_value = field.run_validation(primitive_value)
                # 局部钩子
                if validate_method is not None:
                    validated_value = validate_method(validated_value)
            except ValidationError as exc:
                errors[field.field_name] = exc.detail

        return ret
    
 # 你自己写的序列化类---》继承了ModelSerializer---》继承了Serializer---》BaseSerializer---》Field

断言assert

框架的源码中,会大量使用断言

  • 什么是断言

    assert :断言,作用的判断,断定一个变量必须是xx,如果不是就报错

  • 一般报错写法

    name = 'lqz1'
    if not name == 'lqz':
        raise Exception('name不等于lqz')
    
    print('程序执行完了')
    
  • assert的断言写法

    name = 'lqz1'
    assert name == 'lqz', 'name不等于lqz'
    print('程序执行完了')
    
posted @   Nirvana*  阅读(56)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
点击右上角即可分享
微信分享提示