drf-序列化组件

一、序列化组件介绍

  基于原生django写接口:json格式数据要自己序列化,urlencoded:传过来的数据要用for循环来取出值,在定义成字典的形式,比较麻烦。

序列化组件:drf提供的一个可以做序列化和反序列化的一个类。 我们可以借助于drf提供的序列化组件来完成快速序列化

可以帮助我们:1. 序列化 2. 反序列化 3. 数据校验

如何做序列化:

  1. 写一个类,继承serializers.Serializer

  2. 要写序列化的字段,字段参数

  3. 视图类:

    ser = XxxSerializer(instance=要序列化的对象,many=True)  

  4. ser.data :序列化之后的数据(列表,或者字典)

使用步骤:

1 先在配置文件中注册 :

    INSTALLED_APPS = [

      'rest_framework',

    ]

2. 写一个序列化类:新建一个py文件(serializer.py)

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

  -继承drf提供的serializers.Serializer
  -在类中写要序列化的字段:字段类---》跟之前学过的models.py中的字段类完全对应,但是比models多

Serializer:序列化器

作用:

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

3. 在视图函数中,使用序列化类

  多条:serializer=UserSerializer(instance=users,many=True)

  单条:serializer=UserSerializer(instance=user)

4. 拿到序列化后的数据

  serializer.data  可能是列表(多条),可能是字典(单条)

5.使用drf提供的Response 返回

  from rest_framework.response import Response

二、序列化组件快速使用之序列化

1. 路由

  urlpatterns = [

   path('users/',UserView.as_view()),

  path('users/<int:pk>',UserDetailView.as_view()),

  ]

2. 视图类

from .models import User
from .serializer import UserSerializer
from rest_framework.response import Response
class UserView(APIView):
    def get(self,request):
        users=User.objects.all()
        # 之前用for循环,现在用序列化类
        # 传了两个参数:instance 要序列化的对象(qs,单个对象)many=True表示序列化多条,如果不写就是序列化一条           
        ser=UserSerializer(instance=users,many=True) # 底层通过判断instance这个参数是否有值,值为空:执行create方法;有值:执行update方法
        # 拿到序列化后的数据  ser.data--->多条就是列表  单条字典           
        return Response(ser.data)
class UserDetailView(APIView):
    def get(self,request,pk):       
        user=User.objects.all().filter(pk=pk).first()
        # 传了两个参数:instance 要序列化的对象   many=True表示序列化多条        
       ser=UserSerializer(instance=user)
       return Response(ser.data)            

 3. 序列化类

写序列化类

from rest_framework import serializers

# 继承drf提供的serializers.Serializer
class UserSerializer(serializers.Serializer):
    # 写要序列化的字段
    name = serializers.CharField()
    # hobby = serializers.CharField()
    # password=serializers.CharField()
    age=serializers.IntegerField()

三、常用字段类和参数

1. 常用字段类

写序列化类的时候,写了CharField,IntegerField 跟django中的models中类似

序列化类中的和models中的一一对应,但是序列化中多一些(ListField、DictField

常用字段类型:

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

2. 常用字段参数

字段类上,是可以传参数,是做反序列化校验用的

所有字段都可以用通用的,非常重要:read_only,write_only,default,required,allow_null

选项参数:

参数名称作用
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. 序列化,序列化器会把模型对象转换成字典,经过response以后变成json字符串

  2. 反序列化,把客户端发送过来的数据,经过request以后变成字典,序列化器可以把字典转成模型

  3. 反序列化,完成数据校验功能

反序列化之校验:

ser=BookSerializer(data=request.data)

if ser.is_valid():走三层校验

  1. 字段自己的校验规则(字段类的属性上)

  2. 局部钩子(给某个字段加校验规则)

  3. 全局钩子

else:

  ser.errors # 有错就向前端返回errors

反序列化保存

ser.save():

  必须序列化类中重写create,自己看保存到哪个表

-新增:
ser = BookSerializer(data=request.data)
ser.save()--->触发 序列化类中的 create---》为什么?内部做了判断:根据是否有instance
create中,自己写保存到哪个表中:有数据--》保存到某个表中
-修改
ser = BookSerializer(instance=待修改对象,data=request.data)
ser.save()--->触发 序列化类中的 update---》为什么?内部做了判断:根据是否有instance
update中,有待修改对象,有数据---》修改完保存即可--》两种方式

def create(self, validated_data):  # validated_data:前端传入的校验过后的数据
    user = User.objects.create(**validated_data)
    return user

 补充:函数和方法

函数和方法
普通函数,调用时候,有几个值,就要传几个值

方法:定义在类内部:对象的绑定方法,类的绑定方法----》绑定给谁,谁来调用---》调用的时候,会自动传值
类调用对象绑定方法,就变成了普通函数,有几个值就要传几个值
对象调用类的绑定方法,就是方法,会自动传值

一切皆对象,函数也是个对象, 由某个类产生

# 类调用对象绑定方法,就变成了普通函数,有几个值就要传几个值
# 对象调用类的绑定方法,就是方法,会自动传值

from types import MethodType, FunctionType
# 一切皆对象,函数也是个对象, 由某个类产生     FunctionType
def add():
    pass
# isinstance:判断一个对象是不是这个类的对象
print(isinstance(add, FunctionType)) # True
print(isinstance(add, MethodType)) # False

class Foo:
    def run(self):
        pass

    @classmethod # 绑定给类的方法
    def xx(cls):
        pass

    @staticmethod # 加了静态装饰器,方法就变成了普通函数
    def zz():
        pass


f=Foo() # 对象
# 对象调用 run ,run就是方法 会自动传值
print(isinstance(f.run,FunctionType)) # False
print(isinstance(f.run,MethodType)) # True

# 类来调用run,run就是函数,有几个值就要传几个值
print(isinstance(Foo.run,FunctionType)) # True
print(isinstance(Foo.run,MethodType)) # False


# 类调用类的绑定方法---》就是方法
print(isinstance(Foo.xx,FunctionType)) # False
print(isinstance(Foo.xx,MethodType)) # True

# 对象调用类的绑定方法---》 也是方法 ,会自动将对象的类传入
print(isinstance(f.xx,FunctionType)) # False
print(isinstance(f.xx,MethodType)) # True

# 对象来调用加了静态装饰器的
print(isinstance(f.zz,FunctionType))  # True
print(isinstance(f.zz,MethodType))    #False

# 类来调用
print(isinstance(Foo.zz,FunctionType))  # True
print(isinstance(Foo.zz,MethodType))    #False

 四、APIVIew+Response+序列化类(Book的五个接口)

class BookSerializer(serializers.Serializer):
    name = serializers.CharField()
    price = serializers.IntegerField()
    publish = serializers.CharField()

    # 重写create  前端传入,校验过后的数据validated_data
    def create(self, validated_data):
        book = Book.objects.create(**validated_data)  # 前端传入的key值必须跟数据库字段一致的
        return book

    # 如果是修改,需要重写update方法

    def update(self, instance, validated_data):
        # instance 就是pk  2
        # Book.objects.filter(pk=instance).update(**validated_data)
        # instance 待修改的对象   咱们在 view中的那个book
        # validated_data 校验过后的数据   本质还是  request.data 经过了数据校验
        # 方式一:
        # instance.name=validated_data.get('name')
        # instance.price=validated_data.get('price')
        # instance.publish=validated_data.get('publish')
        # instance.save()

        # 方式二: 反射是通过字符串动态的获取或设置属性或方法
        # get=getattr(self,'get')
        # get()
        # setattr(instance,'name','西游记') ---》 instance.name='西游记'
        for k in validated_data:  
            setattr(instance, k, validated_data.get(k))
        instance.save()
        return instance
class BookDetailView(APIView):
    # 查询单条
    def get(self, request, pk, *args, **kwargs):
        book = Book.objects.all().filter(pk=pk).first()
        ser = BookSerializer(book,many=False)
        return Response({'code': 100, 'msg': '查询单条成功', 'result': ser.data})

        # 修改一条
    def put(self, request, pk, *args, **kwargs):
        # 要用查出来的对象,使用传入的数据,做修改
        book=Book.objects.filter(pk=pk).first()
        ser = BookSerializer(instance=book,data=request.data) # 使用前端传入的数据,修改book
        if ser.is_valid():
            ser.save()  # 调用了序列化类的save:内部会触发序列化类中 update方法的执行     不是book.save()
            return Response({'code': 100, 'msg': '修改成功'})
        else:
            return Response({'code': 100, 'msg': ser.errors})

    def delete(self, request, pk, *args, **kwargs):
        Book.objects.filter(pk=pk).delete()
        return Response({'code': 100, 'msg': '删除成功'})

五、高级用法source(了解)

总结:

  -改变字段的名字
  -跨表查询某个字段
  -表模型中的方法

1. 字段类的属性:

 如下写法,就能修改序列化的字段

class BookSerializer(serializers.Serializer):

用法一:最简单,拿表中的字段

xxx = serializers.CharField(source='name')

用法二 :跨表查询

publish = serializers.CharField(source='publish.name') # 自动对应成出版社的名字 可以通过 .字段名跨表查询 ,因为在Book表中所以不用.book

用法三:表模型中写方法,拿到方法的返回值

yyy = serializers.CharField(source='get_name')

### models.py中

@property # 将get_name方法伪装成了属性

def get_name(self):

return self.name+'sb'

结果:

 

六、高级用法定制字段

定制返回的字段格式:三种方案,目前就讲了两种方案

返回格式:{"name": "信息","price": 12,"publish": {name:xx,addr:xx}}

总结:

  -序列化类中写:SerializerMethodField ---》get_字段名

  -表模型中写方法,包装成数据属性---》序列化类中写跟方法名同名的字段即可:字段类

方案一:在序列化类中写

  1. 写一个字段,该字段所对应的字段类是:SerializerMethodField

  2. 必须对应一个get_字段名的方法,方法中传入一个obj参数(obj:是序列化之后的对象)。该方法返回什么,这个字段对应的值就是什么

案列:

表模型:

 在序列化类中写

from rest_framework import serializers
from .models import Book


class BookSerializer(serializers.Serializer):
    name = serializers.CharField()
    price = serializers.IntegerField()
    # 定制字段
    # 方式一:使用SerializerMethodField
    publish_detail = serializers.SerializerMethodField()
    def get_publish_detail(self,obj): # obj:序列化之后的book对象
        return {'name':obj.publish.name,'addr':obj.publish.addr}

 

方案二:在models(表模型中写)

  1. 在表模型中写一个方法(规范点可以加一个@property装饰器),方法中return一个返回值(字典,列表,字符串)

  2. 在序列化中,使用DictFeild,CharField,ListFeild去接收这个字段的值

 案例:

表模型:

 

 

 在序列化类中接收这个返回值

from rest_framework import serializers
from .models import Book


class BookSerializer(serializers.Serializer):
    name = serializers.CharField()
    price = serializers.IntegerField()
# 方式二:表关系中定制
    publish_dic = serializers.DictField() # DictField():用这个来接收models中传递过来的字典

七、多表关联序列化和反序列化

以后一个序列化类,想既序列化,又做反序列化,会出现问题:字段不匹配,尤其是多表关联的字段。

# 以后,有的字段 即做序列化,又做反序列化
    name = serializers.CharField()
    price = serializers.IntegerField()
# 有的字段:只做序列化(read_only表示 只做序列化)
    publish_dict = serializers.DictField(read_only=True)  # 只做序列化
    author_list = serializers.ListField(read_only=True)  # 只做序列化
    
# 有的字段只做反序列化(write_only=True)-->是什么类型,取决于前端传入的格式什么样
    publish_id = serializers.IntegerField(write_only=True)  # 反序列化
    authors = serializers.ListField(write_only=True)  # 反序列化    
    
# 保存方法需要自己重写
    def create(self, validated_data):  # {"name":"爱做梦","price":999,"publish":1,"authors":[1,2]}
        authors=validated_data.pop('authors')
        book = Book.objects.create(**validated_data)
        # 增加中间表的记录:图书和作者的关系
        book.authors.add(*authors)  # 向中间表中存入:这个图书关联的做作者
        return book

八、反序列化校验总结

什么情况才用反序列化校验? 

  做反序列化的时候,才用校验前端传入的数据

三层校验:

字段自己:字段类属性---》可控制的比较小
局部钩子:单个字段校验
全局钩子:多个字段同时校验

 

posted @ 2023-08-30 18:55  Maverick-Lucky  阅读(37)  评论(0)    收藏  举报