序列化之Serializer类与ModelSerializer类的使用
序列化之Serializer类的使用(5星)
作用:
- 序列化,序列化器会把模型对象转换成字典,经过response以后变成json字符串
- 反序列化,把客户端发送过来的数据,经过request以后变成字典,序列化器可以把字典转成模型
- 反序列化,完成数据校验功能
基本使用
模型类
class Books(models.Model):
    title = models.CharField(max_length=32)
    price = models.IntegerField()
    publish = models.CharField(max_length=32)
序列化类
from rest_framework import serializers
from .models import Books
# 想要序列化和反序列化的book表
class BookSerializer(serializers.Serializer):
    title = serializers.CharField()  # 这里的max_length是限制反序列化时的条件
    price = serializers.IntegerField()
    publish = serializers.CharField()  # 这里的max_length也是限制反序列化时的条件
    # 如果要更新 一定要重写update方法(视图类中调用save方法会自动触发)
    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
    # 如果要新增 一定要重写create方法(视图类中调用save方法会自动触发)
    def create(self, validated_data):
        # 保存
        book_obj = Books.objects.create(**validated_data)
        # 返回
        return book_obj
视图类
from .models import Books
from .serializer import BookSerializer
from rest_framework.views import APIView
from rest_framework.response import Response
class BookAPIView(APIView):
    # 查询所有的接口
    def get(self, request, *args, **kwargs):
        book_list = Books.objects.all()
        # 把queryset对象序列化成字典
        # instance是传要序列化的对象 如果有多条数据对象 那么要加many=True
        ser = BookSerializer(instance=book_list, many=True)
        print(ser.data)
        return Response(ser.data)
    # 新增的接口
    def post(self, request, *args, **kwargs):
	# data参数要传一个字典,正好request.data是一个字典(里面是post请求的数据)
        ser = BookSerializer(data=request.data)
        if ser.is_valid():
		# 不光序列化类里要保存,这里也要save才能保存成功
            ser.save()  # 会触发create方法
            return Response(ser.data)
# 查询单个接口
class BookDetailAPIView(APIView):
    # 查询单个
    def get(self, request, pk):
        # 查出主键值为pk的书
        book_obj = Books.objects.filter(pk=pk).first()
        ser = BookSerializer(instance=book_obj)
        return Response(ser.data)
    def delete(self, request, pk):
        # 删除主键为pk的书
        res = Books.objects.filter(pk=pk).delete()  # res代表影响的行数
        if res:
            return Response()
    def put(self, request, pk):
        # 先查出主键为pk的书
        book_obj = Books.objects.filter(pk=pk)
        # 反序列化 修改数据 既要传对象,又要传修改的数据
        ser = BookSerializer(instance=book_obj, data=request.data)
        # 校验数据
        if ser.is_valid():
            ser.save()  # 会触发update方法
            return Response(ser.data)
路由
urlpatterns = [
    # drf继承APIView的路由
    path('books/', views.BookAPIView.as_view()),
    path('books/<int:pk>', views.BookDetailAPIView.as_view()),
]
序列化类字段和字段参数(2星)
字段类型
# 字段(必须记住)
CharField
IntegerField
FloatField
DecimalField
DateTimeField
DateField
# 知道--》models中没有的---》反序列化的时候,前端传入的
{name:lqz,age:19,hobby:[篮球,足球],wife:{name:lyf,age:38}}
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=) | 
字段参数
# 重点:
read_only:表明该字段仅用于序列化输出,默认False
  -如果read_only=True,这个字段只用来做序列化---》把对象---》json给前端
write_only:表明该字段仅用于反序列化输入,默认False
  -如果read_only=write_only,这个字段只用来做反序列化---》前端json---》存到数据库
选项参数:
| 参数名称 | 作用 | 
|---|---|
| max_length | 最大长度(CharField) | 
| min_lenght | 最小长度(CharField) | 
| allow_blank | 是否允许为空(CharField) | 
| trim_whitespace | 是否截断空白字符(CharField) | 
| max_value | 最小值 (IntegerField) | 
| min_value | 最大值(IntegerField) | 
通用参数:
| 参数名称 | 说明 | 
|---|---|
| read_only | 表明该字段仅用于序列化输出,默认False | 
| write_only | 表明该字段仅用于反序列化输入,默认False | 
| required | 表明该字段在反序列化时必须输入,默认True | 
| default | 反序列化时使用的默认值 | 
| allow_null | 表明该字段是否允许传入None,默认False | 
| validators | 该字段使用的验证器(不太用) | 
| error_messages | 包含错误编号与错误信息的字典 | 
| label | 用于HTML展示API页面时,显示的字段名称 | 
| help_text | 用于HTML展示API页面时,显示的字段帮助提示信息 | 

Serializer高级使用(5星)
source和serializers.SerializerMethodField()的用法
source参数
让序列化的字段映射到表模型的某个字段或方法上,可以使用 . 字段深度取值
1.直接指名道姓的指定对应的字段
class BookSerializer(serializers.Serializer):
    book_title = serializers.CharField(source='title') # 这里指定和book类里的title做对应 等号前就可以随便写了
    price = serializers.IntegerField()
    publish = serializers.CharField()
    
# 也可以.深度取值
class BookSerializer(serializers.Serializer):
    book = serializers.CharField()
    publish = serializers.CharField(sourse='publish.name')   # 该字段就可以跨表拿到publish的名字
2.指定某方法
在model.py内
class Books(models.Model):
    title = models.CharField(max_length=32)
    price = models.IntegerField()
    publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE)
    def publish_detail(self):  # 定义一个展示出版社详细信息的方法
        return {'name': self.publish.name, 'addr': self.publish.addr}
class Publish(models.Model):
    name = models.CharField(max_length=32)
    addr = models.CharField(max_length=32)
    
在自定义序列化py里
class BookSerializer(serializers.Serializer):
    title = serializers.CharField()  
    price = serializers.IntegerField()
    publish = serializers.CharField(source='publish_detail')  # 这个和下面的结果一样 只是两种写法
    publish_detail = serializers.CharField()  # 两个结果都是显示model.py里定义的那个方法的返回值

总结
source可以对应表模型的字段和方法(返回结果是什么,字段就是什么), 对应字段就是指名道姓的一一对应,对应方法一般用来做一对多,多对多的字段返回,如上面跨表得到publish的详细信息
SerializerMethodField
class BookSerializer(serializers.Serializer):
    title = serializers.CharField()
    price = serializers.IntegerField()
    publish = serializers.SerializerMethodField()  # 必须对应一个方法 def get_字段名,不然报错
    def get_publish(self, obj):  # obj是该类要x的对象
        print(obj)
        return {'name': obj.publish.name, 'addr': obj.publish.addr}
    # 然后结果是publish字段就会返回上面你自定义的结果

总结
和source结果一样,只是一个在表模型里面写方法(source),一个是在序列化类里面写方法(SerializerMethodField)
字段本身校验
name=serializers.CharField(max_length=8,min_length=3)
# 这句话就是最大为8 最小为3 不符合条件就不会被入库
局部钩子函数
在该序列化类里
# 局部钩子函数
    def validate_name(self, name):
        if name.startswith('sb'):
            from rest_framework.exceptions import ValidationError
            # 主动抛异常
            raise ValidationError('不能以sb开头')
        else:
            return name

全局钩子函数
# 全局钩子函数
    def validate(self, attrs):  # attrs是一个字典,里面有所有字段的信息{'字段':'数据'}
        if attrs.get('name').endswith('sb') or '日本' in attrs.get('addr'):
            from rest_framework.exceptions import ValidationError
            raise ValidationError('名称和地址不能出现sb和日本')
        return attrs  # 别忘了把数据返回

模型类序列化器ModelSerializer使用(5星)
基本使用
class BookModelSerializer(serializers.ModelSerializer):  # 继承ModelSerializer
    class Meta:
        model = Books  # 和指定的表关联
        
        fields = ['id', 'name', 'publish_detail']  # 指定字段或方法
        fields = '__all__'  # 指定所有字段(不包括方法)
        exclude = ['id']  # 从所有的字段中排除(不包括方法)
        '''
        上面3个只能写一个,看需求使用
        '''
        depth = 1  # 查询深度 效果见下图
        
''' class Meta里的数据就这么多 其他的类似重写字段等等的方法需要和class Meta保持同级,不能在class Meta内部'''

重写字段
class BookModelSerializer(serializers.ModelSerializer):
    # 指定和哪个表作关联
    class Meta:
        model = Books
        fields = '__all__'  # 指定所有字段
    # 重写字段(修改数据)
    title = serializers.SerializerMethodField()
    def get_title(self, obj):  # 和SerializerMethodField用法一样 也必须指定一个方法: get_字段名
        return '经典名著' + obj.title

扩写字段(表模型中没有的字段)
方法一
在models.py内定义一个方法:
    def publish_detail_str(self):
        return {'name': self.publish.name, 'addr': self.publish.addr}
# 多写字段(在序列化类.py中)
    publish_detail = serializers.CharField(source='publish_detail_str')
	code = serializers.CharField(max_length=4, min_length=4) # 只要把新增的字段注册进列表就可以了
# 反序列化新增接收的字段
    phone = serializers.CharField(max_length=20, write_only=True)
    addr = serializers.CharField(max_length=20, write_only=True)

方法二(直接返回另一个序列化类数据)
# 课时序列化类
class CourseSectionSerializer(serializers.ModelSerializer):
    class Meta:
        model = CourseSection
        fields = ['name', 'orders', 'section_link', 'duration', 'free_trail']
# 章节序列化类
class CourseChapterSerializer(serializers.ModelSerializer):
    class Meta:
        model = CourseChapter
        fields = ['id', 'name', 'chapter', 'summary', 'coursesections']
    # 新增一个字段 返回的是某一序列化类返回的字段
    coursesections = CourseSectionSerializer(many=True)  # 站在章节角度看,因为一个章节可以对应多个课时 所以用many=True,如果是对应一个,那么就不需要加
# 这样 coursesections字段返回的就是CourseSectionSerializer序列化类返回的所有字段

反序列化新增接收的字段及应用
class AuthorSerializer(serializers.ModelSerializer):
    class Meta:
        model = Author
        fields = ['id', 'name', 'age', 'phone', 'addr', 'author_detail']
        extra_kwargs = {
            'author_detail': {'read_only': True},
        }
    ##########################新增两个字段用来反序列化接收数据##############################
    phone = serializers.CharField(max_length=20, write_only=True)
    addr = serializers.CharField(max_length=20, write_only=True)
    ########因为和作者详情表是一对一,直接和作者详情表的字段写的一样,这样还能有字段校验########
    ###################重写create方法,在这里面把接收到的数据保存到两张表里##################
    def create(self, validated_data):
        name = validated_data.get('name')
        age = validated_data.get('age')
        phone = validated_data.get('phone')
        addr = validated_data.get('addr')
     ###################先保存作者详情表的,再保存作者表,不然会保存不进去###################
        author_detail_obj = AuthorDetail.objects.create(phone=phone, addr=addr)
        author_obj = Author.objects.create(name=name, age=age, author_detail=author_detail_obj)
        return author_obj
反序列化字段校验
字段本身的校验是映射了表模型中的字段规则
# 在models.py中
class Publish(models.Model):
    name = models.CharField(max_length=32, unique=True)
    addr = models.CharField(max_length=32)
# 如果在序列化类里什么限制都不写 那么会按照字段本身的校验规则


重新定义
class PublishModelSerializer(serializers.ModelSerializer):
    # 指定和哪个表作关联
    class Meta:
        model = Publish
	    fields = '__all__'
    name = serializers.CharField(max_length=1)  # 重新设置字段的校验规则

多对多表使用半自动创建关系的反序列化
要重写create方法和update方法,并且要把外键字段在序列化类里面重写成serializers.ListField(write_only=True),不然新增或者修改的时候会发现后端接收不到该字段的数据
在models.py中
class CarModel(models.Model):
    name = models.CharField(max_length=20)
    price = models.IntegerField()
    # 一对多外键字段
    car_factory = models.ForeignKey(to='CarFactory', on_delete=models.SET_NULL, null=True)
    # 多对多外键字段,用的是半自动
    distributor = models.ManyToManyField(to='Distributor', through='CarModel2Distributor',
                                         through_fields=('car_model', 'distributor'))
# 手动创建的第三张关系表
class CarModel2Distributor(models.Model):
    car_model = models.ForeignKey(to='CarModel', on_delete=models.SET_NULL, null=True)
    distributor = models.ForeignKey(to='Distributor', on_delete=models.SET_NULL, null=True)
class CarFactory(models.Model):
    name = models.CharField(max_length=20)
    addr = models.CharField(max_length=32)
    phone = models.CharField(max_length=11)
class Distributor(models.Model):
    name = models.CharField(max_length=20)
    addr = models.CharField(max_length=32)
    phone = models.CharField(max_length=11)
在序列化类.py中
class OperationCarModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = CarModel
        fields = ['id', 'name', 'price', 'car_factory', 'distributor', 'distributor_args']
        extra_kwargs = {
            'distributor': {'read_only': True}
        }
    distributor_args = serializers.ListField(write_only=True)  # 新增反序列化外键字段
    def create(self, validated_data):
        print(validated_data)
        car_name = validated_data.get('name')
        car_price = validated_data.get('price')
        car_factory = validated_data.get('car_factory')
        distributor_id_list = validated_data.get('distributor_args')
        car_obj = CarModel.objects.create(name=car_name, price=car_price, car_factory=car_factory)
        for distributor_id in distributor_id_list:
            CarModel2Distributor.objects.create(car_model=car_obj, distributor_id=distributor_id)
        return car_obj
在views.py中
class OperationCarAPIView(GenericViewSet, CreateModelMixin, UpdateModelMixin, DestroyModelMixin):
    queryset = CarModel.objects.all()
    serializer_class = OperationCarModelSerializer

局部钩子函数
from rest_framework.exceptions import ValidationError
# 局部钩子
    def validate_name(self, name):
        if name.startswith('sb'):
            raise ValidationError('不能以sb开头')
        return name

全局钩子函数
from rest_framework.exceptions import ValidationError
# 全局钩子函数
    def validate(self, attrs):
        if attrs.get('name').endswith('sb') or '日本' in attrs.get('addr'):
            raise ValidationError('名称和地址不能出现sb和日本')
        return attrs

额外添加参数(extra_kwargs)
class PublishModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = Publish
        fields = '__all__'
        
        # 把字段需要的参数全部写进字典
        extra_kwargs = {'name': {'write_only': True, 'max_length': 8}, 'addr':{...}}

wirte_only和read_only
基本用法
write_only
class PublishModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = Publish
        fields = '__all__'  # 指定所有字段
    name = serializers.CharField(write_only=True)  # 只写(不能读)

read_only
class PublishModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = Publish
        fields = '__all__'  # 指定所有字段
    name = serializers.CharField(read_only=True)  # 只读(不能写)

应用场景
class PublishModelSerializer(serializers.ModelSerializer):
    # 指定和哪个表作关联
    class Meta:
        model = Publish
        fields = '__all__'  # 指定所有字段
    name_detail = serializers.SerializerMethodField(read_only=True)  # 设置只读
    def get_name_detail(self, obj):
        return 'xxx' + obj.name
    name = serializers.CharField(write_only=True)  # 设置只写



 
                
            
         浙公网安备 33010602011771号
浙公网安备 33010602011771号