博客
关于我
drf Serializer基本使用
阅读量:402 次
发布时间:2019-03-06

本文共 22427 字,大约阅读时间需要 74 分钟。

drf序列化

   在前后端不分离的项目中,可以使用Django自带的forms组件进行数据验证,也可以使用Django自带的序列化组件对模型表数据进行序列化。

   那么在前后端分离的项目中,drf也提供了数据验证与序列化,相比于Django原生的序列化它更加强大与易用。

准备工作

注册drf

   首先第一步,我们需要在项目全局文件夹中注册drf

INSTALLED_APPS = [    'app01.apps.App01Config',    'rest_framework', # 注册drf]

模型表

   下面是项目中的模型表。

   学生和班级是一对多

   班级和老师是多对多

   班级和班主任是一对一

from django.db import models# Create your models here.class Student(models.Model):    student_id = models.AutoField(primary_key=True, verbose_name="学生编号")    student_name = models.CharField(max_length=8, verbose_name="学生姓名")    student_gender = models.BooleanField(        choices=([0, "male"], [1, "female"]), default=0, verbose_name="学生性别")    student_class = models.ForeignKey(        to="Classes", verbose_name="所属班级", on_delete=models.CASCADE)  # 一个班级多个学生    def __str__(self):        return self.student_name    class Meta:        db_table = ''        managed = True        verbose_name = 'Student'        verbose_name_plural = 'Students'class Classes(models.Model):    class_id = models.AutoField(primary_key=True, verbose_name="班级编号")    class_name = models.CharField(max_length=8, verbose_name="班级名称")    class_teacher = models.OneToOneField(        to="Teacher", verbose_name="班主任", on_delete=models.CASCADE)  # 一个班级只有一个班主任    def __str__(self):        return self.class_name    class Meta:        db_table = ''        managed = True        verbose_name = 'Classes'        verbose_name_plural = 'Classess'class Teacher(models.Model):    teacher_id = models.AutoField(primary_key=True, verbose_name="教师编号")    teacher_name = models.CharField(max_length=8, verbose_name="教师姓名")    teacher_class = models.ManyToManyField(        to="Classes", verbose_name="任教班级")  # 一个班级中可有多个老师,一个老师也可以在多个班级中任教    def __str__(self):        return self.teacher_name    class Meta:        db_table = ''        managed = True        verbose_name = 'Teacher'        verbose_name_plural = 'Teachers'

数据展示

   学生信息如下:

mysql> select * from app01_student;+------------+--------------+----------------+------------------+| student_id | student_name | student_gender | student_class_id |+------------+--------------+----------------+------------------+|          1 | 学生1        |              1 |                1 ||          2 | 学生2        |              1 |                2 ||          3 | 学生3        |              1 |                2 ||          4 | 学生4        |              0 |                1 |+------------+--------------+----------------+------------------+

   教师信息如下:

mysql> select * from app01_teacher;+------------+--------------+| teacher_id | teacher_name |+------------+--------------+|          1 | 王老师       ||          2 | 李老师       ||          3 | 张老师       |+------------+--------------+

   班级信息如下:

mysql> select * from app01_classes;+----------+--------------+------------------+| class_id | class_name   | class_teacher_id |+----------+--------------+------------------+|        1 | 高一一班     |                1 ||        2 | 高二一班     |                2 |+----------+--------------+------------------+

   教师与班级关系如下:

mysql> select * from app01_teacher_teacher_class;+----+------------+------------+| id | teacher_id | classes_id |+----+------------+------------+|  1 |          1 |          1 ||  2 |          1 |          2 ||  3 |          2 |          1 ||  4 |          2 |          2 ||  5 |          3 |          1 ||  6 |          3 |          2 |+----+------------+------------+

url书写

   接下来书写url,以查询学生模型表为例,为了符合framework规范,所以要有一个有名分组来接收可能获取/修改/删除的编号。

from django.conf.urls import urlfrom django.contrib import adminfrom app01 import viewsurlpatterns = [    url(r'^admin/', admin.site.urls),    url(r'^api/students/(?P
\d+)?', views.Student.as_view()),]# 添加问号,代表可有,可没有

自定义序列器

   使用自定义序列器。可以更加灵活的使用序列化及反序列化。也是推荐使用的方式。

创建序列类

   序列类的作用如下:

  1. 序列化时,可选择序列化的模型表字段
  2. 反序列化时,可选择对数据验证的规则,类似于forms组件
from .models import Studentfrom rest_framework import serializersclass StudentSerializer(serializers.Serializer):    student_id = serializers.CharField()    student_name = serializers.CharField()    student_gender = serializers.BooleanField()    student_class = serializers.CharField()

进行序列化

   接下来进行序列化,首先要书写视图函数。

   由于我们每次都需要返回一个状态码,以及内容包装,所以可以创建一个构建返回内容的类。

   第一步要将序列化类导入,在进行序列化的时候将需要序列化的数据对象进行传入。

   当序列化完成后,得到一个序列化对象,它有一个data属性,是已经序列化完成的一个字典。

   把字典返回,如果不使用rest_framework提供的Response,就得使用JsonResponse

   序列化单个对象,不需要在序列化时指定参数many

   序列化多个对象,需要在序列化时指定参数many

from rest_framework.views import APIViewfrom rest_framework.views import Request  from rest_framework.views import Response # 通过Response返回from app01 import modelsfrom app01.drf_ser import StudentSerializerclass returnData(object):	# 构建返回的信息    def __init__(self):        self.status = 100  # 100代表成功,200代表失败        self.message = None  # 返回的信息        self.data = None  # 序列化后的结果    def get_dict(self):        return self.__dict__    @property    def data(self):        return self.data    @data.setter    def data(self, value):        self.__dict__["data"] = valueclass Student(APIView):    def get(self, request, sid=None):        # 初始化返回信息        res = returnData()        if sid:            # 代表获取单个            obj = models.Student.objects.filter(pk=sid).first()            if obj:                serializer_result = StudentSerializer(obj)                  res.data = serializer_result.data            else:                res.message = "没有该学生"                res.status = 200            return Response(res.get_dict())        else:            # 代表获取所有            obj_queryset = models.Student.objects.all()            if obj_queryset:                serializer_result = StudentSerializer(                    obj_queryset, many=True)   # many=True代表获取多条                res.data = serializer_result.data            else:                res.message = "暂时没有任何学生"                res.status = 200            return Response(res.get_dict())    def post(self, request, sid):        # 新增        pass    def delete(self, request, sid):        # 删除        pass    def patch(self, request, sid):        # 修改        pass

   序列化时,只关心怎样将数据返回给页面。

   所以我们只做GET部分,注意用if判断来判定是获取所有,还是获取单个。

   以下是获取全部的结果:

# http://127.0.0.1:8000/api/students/{    "status": 100,    "message": null,    "data": [        {            "student_id": "1",            "student_name": "学生1",            "student_gender": true,            "student_class": "高一一班"        },        {            "student_id": "2",            "student_name": "学生2",            "student_gender": true,            "student_class": "高二一班"        },        {            "student_id": "3",            "student_name": "学生3",            "student_gender": true,            "student_class": "高二一班"        },        {            "student_id": "4",            "student_name": "学生4",            "student_gender": false,            "student_class": "高一一班"        }    ]}

   以下是获取单条的结果:

{    "status": 100,    "message": null,    "data": {        "student_id": "1",        "student_name": "学生1",        "student_gender": true,        "student_class": "高一一班"    }}

   当获取出错时,message中就会存在错误信息:

{    "status": 200,    "message": "没有该学生",    "data": null}

反序列化参数介绍

   反序列化通常是使用POST/PATCH插入或更新数据时使用,收集到页面传递的数据,进行反序列化后写入数据库中。

   当进行反序列化时,可以在序列类中指定一些参数,对将要反序列化写入模型表的字段进行检查。

参数 描述
max_length 最大长度
min_lenght 最小长度
allow_blank 是否允许为空
trim_whitespace 是否截断空白字符
max_value 最小值
min_value 最大值
required 表明该字段在反序列化时必须输入,默认True
default 反序列化时使用的默认值
allow_null 表明该字段是否允许传入None,默认False
validators 该字段使用的验证器
error_messages 包含错误编号与错误信息的字典

   初此之外,还有两个比较特殊的参数:

参数 描述
read_only 表明该字段仅用于序列化输出,默认False,如果设置成True,postman中可以看到该字段,修改时,不需要传该字段
write_only 表明该字段仅用于反序列化输入,默认False,如果设置成True,postman中看不到该字段,修改时,该字段需要传

反序列化钩子

   validate_字段名是局部钩子

   validate是全局钩子

   如果要在钩子中抛出异常,则需要导入异常模块。

from rest_framework import exceptions# raise exceptions.ValidationError('异常了')

   如下是局部钩子的使用示例,因为页面提交过来的数据关于一对多中的班级字段是字符串,所以我们需要将字符串变为模型表对象,方便后面的创建以及更新。

def validate_student_class(self, data):    	# data是提交过来的这一个字段的数据        class_obj = Classes.objects.filter(class_name=data).first()        if not class_obj:            raise exceptions.ValidationError("班级不存在")        data = class_obj  # 将字符串替换为对象        return data

   全局钩子使用也是一样。如下,验证学生名和班级名是否相同,如果相同则抛出异常

def validate(self, validate_data):        student_name = validate_data.get("student_name")        class_obj = validate_data.get("student_class")  # 由于局部钩子中,这里被替换成了对象,所以我们拿到对象不能直接作比较        if student_name == class_obj.class_name:            raise exceptions.ValidationError("学生名不能和班级名相同")        return validate_data

create&update

   在进行反序列化时,必须在序列类中覆写create()方法以及update()方法。

   其中create()方法针对的是新增用户,而update()方法针对的是更新用户。

   如果不进行覆写,则会抛出异常,这是因为作者在源码中做了接口约束的设置:

def update(self, instance, validated_data):	raise NotImplementedError('`update()` must be implemented.')def create(self, validated_data):	raise NotImplementedError('`create()` must be implemented.')

反序列化案例

   了解了上面反序列化需要注意的事项后,开始书写视图函数中的POST/PATCH方法。

   下面是创建一个学生的例子:

def post(self, request):    # 初始化返回信息    res = returnData()    serializer_result = StudentSerializer(data=request.data)  # 传入request.data即可。这里一定要使用关键字传参!!!    if serializer_result.is_valid():        # 验证通过了        serializer_result.save()  # 保存序列化实例类        res.data = serializer_result.data  # 遵循规范,返回新增的数据    else:        # 验证没通过        res.status = 200        res.message = "数据校验失败"        res.data = serializer_result.errors # 添加错误信息        return Response(res.get_dict())

   重写create()方法并返回:

def create(self, validated_data):        instance = Student.objects.create(**validated_data)        return instance  # 这里返回的信息会返回到序列类对象的data属性中

   下面是修改一个学生的例子:

def patch(self, request, sid):        # 初始化返回信息        res = returnData()        obj = models.Student.objects.filter(pk=sid).first()        if obj:            serializer_result = StudentSerializer(instance=obj, data=request.data)  # 需要传入的参数,记录本身,新的数据            if serializer_result.is_valid():                # 验证通过了                serializer_result.save()  # 保存序列化实例类                res.data = serializer_result.data  # 遵循规范,返回修改的数据            else:                # 验证没通过                res.status = 200                res.message = "数据校验失败"                res.data = serializer_result.errors  # 添加错误信息        else:                res.status = 200                res.message = "用户不存在"        return Response(res.get_dict())

   重写update()方法并返回:

def update(self, instance, validated_data):            # 对数据做更新后再返回        # validated_data中取出str的键,然后用反射进行设置                for k, v in validated_data.items():            setattr(instance, k, v)        instance.save()        return instance

全部代码

   views.py

from rest_framework.views import APIViewfrom rest_framework.views import Requestfrom rest_framework.views import Response  # 通过Response返回from app01 import modelsfrom app01.drf_ser import StudentSerializer  # 导入自定义序列类class returnData(object):    # 构建返回的信息    def __init__(self):        self.status = 100  # 100代表成功,200代表失败        self.message = None  # 返回的信息        self.data = None  # 序列化后的结果    def get_dict(self):        return self.__dict__    @property    def data(self):        return self.data    @data.setter    def data(self, value):        self.__dict__["data"] = valueclass Student(APIView):    def get(self, request, sid=None):        # 初始化返回信息        res = returnData()        if sid:            # 代表获取单个            obj = models.Student.objects.filter(pk=sid).first()            if obj:                serializer_result = StudentSerializer(obj)                res.data = serializer_result.data            else:                res.message = "没有该学生"                res.status = 200            return Response(res.get_dict())        else:            # 代表获取所有            obj_queryset = models.Student.objects.all()            if obj_queryset:                serializer_result = StudentSerializer(                    obj_queryset, many=True)   # many=True代表获取多条                res.data = serializer_result.data            else:                res.message = "暂时没有任何学生"                res.status = 200            return Response(res.get_dict())    def post(self, request):        # 初始化返回信息        res = returnData()        serializer_result = StudentSerializer(            data=request.data)  # 传入request.data即可        if serializer_result.is_valid():            # 验证通过了            serializer_result.save()  # 保存序列化实例类            res.data = serializer_result.data  # 遵循规范,返回新增的数据        else:            # 验证没通过            res.status = 200            res.message = "数据校验失败"            res.data = serializer_result.errors  # 添加错误信息        return Response(res.get_dict())    def delete(self, request):        # 删除        pass    def patch(self, request, sid):        # 初始化返回信息        res = returnData()        obj = models.Student.objects.filter(pk=sid).first()        if obj:            serializer_result = StudentSerializer(instance=obj, data=request.data)  # 需要传入的参数,记录本身,新的数据            if serializer_result.is_valid():                # 验证通过了                serializer_result.save()  # 保存序列化实例类                res.data = serializer_result.data  # 遵循规范,返回修改的数据            else:                # 验证没通过                res.status = 200                res.message = "数据校验失败"                res.data = serializer_result.errors  # 添加错误信息        else:                res.status = 200                res.message = "用户不存在"        return Response(res.get_dict())

   自定义序列类

from .models import Studentfrom .models import Classesfrom rest_framework import serializersfrom rest_framework import exceptionsclass StudentSerializer(serializers.Serializer):    student_id = serializers.CharField(read_only=True)  # 创建/修改时不用传该字段,但是页面可以看见    # 相反的,如果是wrtie_only则代表页面看不见,但是你要传    student_name = serializers.CharField(max_length=8, min_length=3)    student_gender = serializers.BooleanField()    student_class = serializers.CharField()    def validate_student_class(self, data):        class_obj = Classes.objects.filter(class_name=data).first()        if not class_obj:            raise exceptions.ValidationError("班级不存在")        data = class_obj  # 将字符串替换为对象        return data    def validate(self, validate_data):        student_name = validate_data.get("student_name")        class_obj = validate_data.get("student_class")        if student_name == class_obj.class_name:            raise exceptions.ValidationError("学生名不能和班级名相同")        return validate_data    def create(self, validated_data):        instance = Student.objects.create(**validated_data)        return instance  # 这里返回的信息会返回到序列类对象的data属性中    def update(self, instance, validated_data):        # 对数据做更新后再返回        # validated_data中取出str的键,然后用反射进行设置        for k, v in validated_data.items():            setattr(instance, k, v)        instance.save()        return instance

模型表序列器

   模型表的序列器定制化比较低,但是使用较为方便。

   能够非常快速的开发接口。

创建序列类

   创建序列器:

# 模型表序列器class StudentModelSerializer(serializers.ModelSerializer):    class Meta:        model = Student  # 对应上models.py中的模型        fields = '__all__'  # 序列化所有字段        # fields=('student_id','student_name') # 只序列化指定的字段        # exclude=('student_id',) # 跟fields不能同时都写,写谁,就表示排除谁        # read_only_fields=('student_id',)        # write_only_fields=('student_class',) # 弃用了,使用extra_kwargs        # extra_kwargs = {  # 类似于这种形式  student_name=serializers.CharField(max_length=16,min_length=4)        #    'student_name': {'write_only': True},        # }    def validate_student_class(self, data):        class_obj = Classes.objects.filter(class_name=data).first()        if not class_obj:            raise exceptions.ValidationError("班级不存在")        data = class_obj  # 将字符串替换为对象        return data    def validate(self, validate_data):        student_name = validate_data.get("student_name")        class_obj = validate_data.get("student_class")        print(class_obj)        if student_name == class_obj.class_name:            raise exceptions.ValidationError("学生名不能和班级名相同")        return validate_data

基本使用

   其他使用一模一样,注意在反序列化时不需要重写create()updata()方法了。

   以下是视图API接口中的代码,只需要把原本使用自定义序列器的地方修改成使用模型表序列器即可。

from rest_framework.views import APIViewfrom rest_framework.views import Requestfrom rest_framework.views import Response  # 通过Response返回from app01 import models# from app01.drf_ser import StudentModelSerializerfrom app01.drf_ser import StudentModelSerializerclass returnData(object):    # 构建返回的信息    def __init__(self):        self.status = 100  # 100代表成功,200代表失败        self.message = None  # 返回的信息        self.data = None  # 序列化后的结果    def get_dict(self):        return self.__dict__    @property    def data(self):        return self.data    @data.setter    def data(self, value):        self.__dict__["data"] = valueclass Student(APIView):    def get(self, request, sid=None):        # 初始化返回信息        res = returnData()        if sid:            # 代表获取单个            obj = models.Student.objects.filter(pk=sid).first()            if obj:                serializer_result = StudentModelSerializer(obj)                res.data = serializer_result.data            else:                res.message = "没有该学生"                res.status = 200            return Response(res.get_dict())        else:            # 代表获取所有            obj_queryset = models.Student.objects.all()            if obj_queryset:                serializer_result = StudentModelSerializer(                    obj_queryset, many=True)   # many=True代表获取多条                res.data = serializer_result.data            else:                res.message = "暂时没有任何学生"                res.status = 200            return Response(res.get_dict())    def post(self, request):        # 初始化返回信息        res = returnData()        serializer_result = StudentModelSerializer(            data=request.data)  # 传入request.data即可        if serializer_result.is_valid():            # 验证通过了            serializer_result.save()  # 保存序列化实例类            res.data = serializer_result.data  # 遵循规范,返回新增的数据        else:            # 验证没通过            res.status = 200            res.message = "数据校验失败"            res.data = serializer_result.errors  # 添加错误信息        return Response(res.get_dict())    def delete(self, request):        # 删除        pass    def patch(self, request, sid):        # 初始化返回信息        res = returnData()        obj = models.Student.objects.filter(pk=sid).first()        if obj:            serializer_result = StudentModelSerializer(instance=obj, data=request.data)  # 需要传入的参数,记录本身,新的数据            if serializer_result.is_valid():                # 验证通过了                serializer_result.save()  # 保存序列化实例类                res.data = serializer_result.data  # 遵循规范,返回修改的数据            else:                # 验证没通过                res.status = 200                res.message = "数据校验失败"                res.data = serializer_result.errors  # 添加错误信息        else:                res.status = 200                res.message = "用户不存在"        return Response(res.get_dict())

多关系子序列化

source参数

   source参数的使用:

  1. 可以改字段名字 xxxx=serializers.CharField(source='student_name')
  2. 可以.跨表 cls=serializers.CharField(source='student_class.name') # 相当于 student_student_class__name进行数据获取
  3. 可以执行方法pub_date=serializers.CharField(source='test') test是Student表模型中的方法(可忽略,这个相当于验证方法。还不如使用钩子函数)

   该参数最重要的两点,source中写的是什么,就从哪里取数据,即展示的就是什么。当反序列化时,不再按照序列器类的字段名进行反序列化,而是按照该参数进行反序列化填值。

   看一下,我要让student_name显示的不是学生的名字,而是班主任的名字,就用到了第一条和第二条,跨表,显示数据,可以这样设置。

class StudentSerializer(serializers.Serializer):    student_id = serializers.CharField(read_only=True)  # 创建/修改时不用传该字段,但是页面可以看见    student_name = serializers.CharField(max_length=8, min_length=3,source="student_class.class_teacher.teacher_name")  # 相当于:Student.objects.filter(pk=传入的id).values_list("student_class__class_teacher__teacher_name")[0][0]    student_gender = serializers.BooleanField(source="student_gender")    student_class = serializers.CharField()

   当进行GET请求后,将会看到下面的结果:

# http://127.0.0.1:8000/api/students/5/{    "status": 100,    "message": null,    "data": {        "student_id": "5",        "student_name": "王老师",  # 所以说,该参数后面写的是什么,展示的就是什么。        "student_gender": false,        "student_class": "高一一班"    }}

   示例演示,我们通常会将展示的数据名字进行重命名,区分开与数据库存储的字段名,这样做更加安全,所以可以进行如下设置:

from .models import Studentfrom .models import Classesfrom rest_framework import serializersfrom rest_framework import exceptionsclass StudentSerializer(serializers.Serializer):    sid = serializers.CharField(read_only=True,source="student_id")  # 创建/修改时不用传该字段,但是页面可以看见    name = serializers.CharField(max_length=8, min_length=3,source="student_name")    gender = serializers.BooleanField(source="student_gender")    classes = serializers.CharField(source="student_class")  # source中写的是什么,就从哪里取数据    def validate_classes(self, data):    	# data是提交过来的这一个字段的数据        class_obj = Classes.objects.filter(class_name=data).first()        if not class_obj:            raise exceptions.ValidationError("班级不存在")        data = class_obj  # 将字符串替换为对象        return data    def create(self, validated_data):        instance = Student.objects.create(**validated_data)        return instance  # 这里返回的信息会返回到序列类对象的data属性中    def update(self, instance, validated_data):        # 对数据做更新后再返回        # validated_data中取出str的键,然后用反射进行设置        for k, v in validated_data.items():            setattr(instance, k, v)        instance.save()        return instance
{    "status": 100,    "message": null,    "data": {        "sid": "5",        "name": "修改学生5",        "gender": false,        "classes": "高一一班"    }}

SerializerMethodField字段

   它需要有个配套方法,方法名叫get_字段名,返回值就是要显示的东西。

   比如,我们想查看每个学生都有那些老师在教授,就可以使用该参数:

class StudentSerializer(serializers.Serializer):    sid = serializers.CharField(read_only=True,source="student_id")  # 创建/修改时不用传该字段,但是页面可以看见    name = serializers.CharField(max_length=8, min_length=3,source="student_name")    gender = serializers.BooleanField(source="student_gender")    classes = serializers.CharField(source="student_class")  # 现在,我要让他显示的是班级编号,而不再是班级名称了    students = serializers.SerializerMethodField()    def get_students(self,instance):        teacher_queryset = instance.student_class.teacher_set.values("pk","teacher_name")        return teacher_queryset

   最后的结果如下:

# http://127.0.0.1:8000/api/students/5/{    "status": 100,    "message": null,    "data": {        "sid": "5",        "name": "修改学生5",        "gender": false,        "classes": "高一一班",        "students": [            {                "pk": 1,                "teacher_name": "王老师"            },            {                "pk": 2,                "teacher_name": "李老师"            },            {                "pk": 3,                "teacher_name": "张老师"            }        ]    }}

转载地址:http://ernkz.baihongyu.com/

你可能感兴趣的文章
[故障公告]博客站点1台负载均衡遭遇流量攻击,造成联通与移动用户无法正常访问
查看>>
上周热点回顾(5.1-5.7)
查看>>
云计算之路-阿里云上:14:20-14:55博客后台2台服务器都CPU 100%引发的故障
查看>>
上周热点回顾(6.19-6.25)
查看>>
云计算之路-阿里云上:docker swarm 集群故障与异常
查看>>
上周热点回顾(2.19-2.25)
查看>>
云计算之路-阿里云上:博客web服务器轮番CPU 100%
查看>>
云计算之路-阿里云上:服务器CPU 100%问题是memcached连接数限制引起的
查看>>
上周热点回顾(3.26-4.1)
查看>>
上周热点回顾(6.25-7.1)
查看>>
【故障公告】10:30-10:45 左右 docker swarm 集群节点问题引发故障
查看>>
工作半年的思考
查看>>
不可思议的纯 CSS 滚动进度条效果
查看>>
【CSS进阶】伪元素的妙用--单标签之美
查看>>
惊闻NBC在奥运后放弃使用Silverlight
查看>>
IE下尚未实现错误的原因
查看>>
创建自己的Docker基础镜像
查看>>
HTTP 协议图解
查看>>
Python 简明教程 --- 20,Python 类中的属性与方法
查看>>
Python 简明教程 --- 21,Python 继承与多态
查看>>