drf基本使用(2)_Serializer的使用

反序列化

数据校验

使用序列化器进行反序列化时,需要对数据进行验证后,才能获取验证成功的数据或保存成模型类对象。

在获取反序列化的数据前,必须调用is_valid()方法进行验证,验证成功返回True,否则返回False。

验证失败,可以通过序列化器对象的errors属性获取错误信息,返回字典,包含了字段和字段的错误。如果是非字段错误,可以通过修改REST framework配置中的NON_FIELD_ERRORS_KEY来控制错误字典中的键名。

验证成功,可以通过序列化器对象的validated_data属性获取数据。获取的是一个有序的字典类型的数据

在定义序列化器时,指明每个字段的序列化类型和选项参数,本身就是一种验证行为。

为了方便演示效果,我们单独创建一个子应用student。

python manage.py startapp student 

创建我们的模型类

from django.db import models

# Create your models here.
class Student(models.Model):
    # 模型字段
    name = models.CharField(max_length=100,verbose_name="姓名",help_text='提示文本:不能为空')
    sex = models.BooleanField(default=1,verbose_name="性别")
    age = models.IntegerField(verbose_name="年龄")
    class_null = models.CharField(max_length=5,verbose_name="班级编号")
    description = models.TextField(max_length=1000,verbose_name="个性签名")

    class Meta:
        db_table="tb_student"
        verbose_name = "学生"
        verbose_name_plural = verbose_name


validators自定义校验函数

在student应用下创建一个序列化器

虽然我们序列化器的字段中提供了一些校验方法,但还是不能满足我们的需求,这个时候你可以在字段中添加validators参数补充验证行为

这里我们定义了一个check_name函数对输入的名字进行校验

from rest_framework import serializers

# 自定义校验函数
def check_name(val):
    import re
    if not re.match('a', val):  # 如果不是以a开头的直接抛出异常,被serializer.error捕获到
        raise serializers.ValidationError('名字不是以a开头的')
    return val

class StudentSerializer(serializers.Serializer):
    # 需要进行数据转转的字段
    id = serializers.IntegerField(read_only=True)
    # 使用validators进行校验
    name = serializers.CharField(validators=[check_name,])
    sex = serializers.BooleanField(default=1)
    age = serializers.IntegerField(max_value=30)
    class_null = serializers.CharField(required=False)  # 这个字段不填的话orm会自动向我们的数据库中添加一个空字符串
   

views视图

from django.http import JsonResponse
from rest_framework.views import APIView
from student.serializer import StudentSerializer
from student import models


class StudentAPIView(APIView):
    def get(self, request):
        student = models.Student.objects.all()
        # 进行序列化
        serializer = StudentSerializer(instance=student, many=True) # 对多条数据进行序列化时指定many=True
        # 序列化多条数据得到的是列表套字典,所以要指定safe=False
        return JsonResponse(serializer.data,safe=False,json_dumps_params={'ensure_ascii': False})

    def post(self, request):
        print(request.data) #{'name': 'bb哥', 'age': 12, 'sex': 0}
        serializer = StudentSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)  # 如果返回False,直接抛出异常,,前端返回400错误
        if serializer.is_valid():
            # 成功后获取数据
            print(serializer.validated_data)
            return JsonResponse({'message':'ok'})
        print('错误信息', serializer.errors)
        return JsonResponse({'error':'校验失败'})

is_valid()方法还可以在验证失败时抛出异常serializers.ValidationError,可以通过传递raise_exception=True参数开启,REST framework接收到此异常,会向前端返回HTTP 400 Bad Request响应。

局部钩子校验validate_字段名

局部钩子就是在序列化器中定义的一个函数,函数名为vallidatae_字段名

反序列化的时的执行顺序:

字段1的参数效验=> 字段1的局部构子校验=>字段2的参数校验,=>字段2的局部钩子校验

class StudentSerializer(serializers.Serializer):
    id = serializers.IntegerField(read_only=True)
    name = serializers.CharField(validators=[check_name,])
    sex = serializers.BooleanField(default=1)
    age = serializers.IntegerField(max_value=30)
    class_null = serializers.CharField(required=False)

    # 局部钩子校验  执行顺序在字段中的校验之后,也是自动触发的
    def validate_age(self, data):
        if data > 20:
            raise serializers.ValidationError('老女人')
        return data
  • views中打印的错误信息
print('错误信息', serializer.errors)
# 错误信息 {'age': [ErrorDetail(string='老女人', code='invalid')]}

全局钩子校验 validate

在序列化器中需要同时对多个字段就行比较时,可以定义validate方法来验证

validate在所有的校验之后自动触发执行

class StudentSerializer(serializers.Serializer):
    id = serializers.IntegerField(read_only=True)
    name = serializers.CharField(validators=[check_name,])
    sex = serializers.BooleanField(default=1)
    age = serializers.IntegerField(max_value=30)
    class_null = serializers.CharField(required=False)  #
    # 比如我们要对两次密码进行教验
    p1 = serializers.CharField()
    p2 = serializers.CharField()

    def validate_age(self, data):
        if data > 20:
            raise serializers.ValidationError('老女人')
        return data

    # 全局钩子校验
    def validate(self, data):  # data是所有的校验完成之后返回的一个有序字典
        print('>>',data)
        # >> OrderedDict([('name', 'aa哥'), ('sex', False), ('age', 18), ('p1', 123), ('p2', 222)])
        p1 = data.get('p1')
        p2 = data.get('p2')
        if p1 != p2:
            raise serializers.ValidationError('两次密码不一致')
        return data

删除传过来的不需要的字段

上面我们对两次密码进行了校验,但是我们对数据进行保存的时候,只需要一个就够了

# 删除方式一: 使用pop删除
我们需要对字段进行修改才能使用pop方法 
password = serializers.CharField(max_length=5, wirte_only=True) # 加上wirte_only的字段可以直接删除
# 在视图中
    serializer.validated_data.pop('password')
# 删除方式二: 重写create方法 
后端通过save()方法保存数据的时候,会触发ModelSerializer中的create方法

    def create(self, validated_data):  # validated_data 是反序列化之后的数据 
        validated_data.pop('password')
        instance = super().create(validated_data)
        return instance 

serializer对象中的context参数

比如我们想要在全局钩子中获取一下当前的请求路径,怎么搞呢

我们可以通过context传递这个参数, 当我们序列化器执行is_valid()的时候 就会执行序列化器中的全局钩子 , 我们可以通过self.context获取

serializer = AccountSerializer(account, context={'request': request})

  • views
    def post(self, request):
        print(request.data)
        serializer = StudentSerializer(data=request.data,context={'request': request}) # 传递request对象

        if serializer.is_valid():
            return JsonResponse({'message':'ok'})
        print('错误信息', serializer.errors)
        return JsonResponse({'error':'校验失败'})
  • serializer

这里获取到的request是经过rest_framework的APIView重新封装后的request对象了

class StudentSerializer(serializers.Serializer):

    id = serializers.IntegerField(read_only=True)
    name = serializers.CharField(validators=[check_name,])
    sex = serializers.BooleanField(default=1)
    age = serializers.IntegerField(max_value=30)
    class_null = serializers.CharField(required=False)  

    p1 = serializers.IntegerField()
    p2 = serializers.IntegerField()
    
    # 全局钩子校验
    def validate(self, data):  
        print('context',self.context)  # context {'request': <rest_framework.request.Request: POST '/stu/students/'>}
        
        request = self.context.get('request')
        print(request.path)  # /stu/students/
        
        p1 = data.get('p1')
        p2 = data.get('p2')
        if p1 != p2:
            raise serializers.ValidationError('两次密码不一致')
        return data

反序列化后保存数据

这里我们重新创建一个应用进行添加数据

方式一 : 直接保存,通过create

  • 创建序列化器
from rest_framework import serializers


class AvSerializer(serializers.Serializer):
    id = serializers.IntegerField(read_only=True)
    name = serializers.CharField(allow_blank=False, allow_null=False)
    age = serializers.IntegerField()
    sex = serializers.BooleanField()
    class_null = serializers.CharField()
    description = serializers.CharField()

  • views
from student import models
from ser.serializer import AvSerializer
from django.http import JsonResponse


class AvAPIView(APIView):
    def get(self, request):
        student = models.Student.objects.get(id=1)
        serializer = AvSerializer(instance=student)
        return JsonResponse(serializer.data, safe=False, json_dumps_params={'ensure_ascii': False})

    def post(self, request):
        print(request.data) # Apiview帮我们处理数据到data中
        serializer = AvSerializer(data=request.data)
        if serializer.is_valid():
            data = serializer.validated_data  # 序列化后的数据
            # 方式一 直接保存
            models.Student.objects.create(**data)
            return JsonResponse({'message': '添加成功'})
        print(serializer.errors)
        return JsonResponse({'error': '校验失败'})

方式二

通过serializer对象的save()方法来触发序列化器中的create来保存

  • views
from rest_framework.views import APIView
from student import models
from ser.serializer import AvSerializer
from django.http import JsonResponse


class AvAPIView(APIView):
    def get(self, request):
        student = models.Student.objects.get(id=1)
        serializer = AvSerializer(instance=student)
        return JsonResponse(serializer.data, safe=False, json_dumps_params={'ensure_ascii': False})

    def post(self, request):
        serializer = AvSerializer(data=request.data)
        if serializer.is_valid():
            data = serializer.save() # 在执行AcSerializer中的create,data是接受返回的数据
            # 序列化后在返回i给前端
            res = AvSerializer(instance=data)
            return JsonResponse(res.data, safe=False)
        return JsonResponse({'error': '校验错误'})


  • serializer.py
from rest_framework import serializers
from student import models

class AvSerializer(serializers.Serializer):
    id = serializers.IntegerField(read_only=True)
    name = serializers.CharField(allow_blank=False, allow_null=False)
    age = serializers.IntegerField()
    sex = serializers.BooleanField()
    class_null = serializers.CharField()
    description = serializers.CharField()

    # save自动触发
    def create(self, validated_data):
        print(validated_data) # 校验成功后的数据
        data = models.Student.objects.create(**validated_data)
        return data # 记得添加后一定要返回

可以看到我们添加成功了,前端也返回了我们添加的数据

更新数据

这里我们用postman提交put请求更新数据, 携带着id提交,

然后后端根据id获取到模型对象进行更新一下用户的名字

方式一: 直接更新

  • views
class AvAPIView(APIView):  
    def put(self, request):
        id = request.data.get('id')  # 不要忘记了这里使用request.data是因为我们使用了APIView
        student_query = models.Student.objects.filter(pk=id)
        serializer = AvSerializer(data=request.data, partial=True)
        # partial=True进行部分字段校验,传递过来哪个字段就校验哪个字段,没有传递的不校验
        if serializer.is_valid():
            student_query.update(**serializer.validated_data)  # update返回的是更新的条数
            student_obj = student_query.first()  # student_obj 是更新后的记录对象
            serializer_new = AvSerializer(instance=student_obj)  # 将更新后的在序列号后返回
            return JsonResponse(serializer_new.data)
        return JsonResponse({'error':'更新失败'})

方式二 save方法

  • serializer
class AvSerializer(serializers.Serializer):  
  # instance 是老的模型类对象
    def update(self, instance, validated_data):
        instance.name = validated_data.get('name')
        instance.age = validated_data.get('age')
        instance.save()  # orm的save方法
        print(instance)
        return instance

  • views
class AvAPIView(APIView):
    def put(self, request):
        id = request.data.get('id')
        student_query = models.Student.objects.filter(pk=id)
        student_obj = student_query.first()
        # 创建序列化器对象
        serializer = AvSerializer(data=request.data,partial=True, instance=student_obj)
        # 实例化序列化器对象时,如果传递的instance是模型类对象,那么通过serializer.save会触发执行类中的update方法
        if serializer.is_valid():
            data = serializer.save()
            serializer_new = AvSerializer(instance=data)
            return JsonResponse(serializer_new.data)
        return JsonResponse({'error': '更新失败'})
posted @ 2020-12-16 13:37  死里学  阅读(178)  评论(0)    收藏  举报