在返回json数据时,整合DRF后并没有取出QuerySet中的数据,再转字典,也能直接返回json,这就是序列化器的功劳。

  • 序列化,序列化器会把模型对象转换成字典,经过response以后变成json字符串
  • 反序列化,把客户端发送过来的数据,经过request以后变成字典,序列化器可以把字典转成模型
  • 反序列化,完成数据校验功能
1
2
3
4
5
6
7
8
class StudentModelSerializer(serializers.ModelSerializer):
class Meta:
model = Student
# 序列化字段
# 全部
fields = '__all__'
# 一个一个写
# fields =['id','name']

student序列化器继承了 serializers.ModelSerializer,这是转为数据库中的表设计的,一一对应,帮我们重写了create()和update()方法,所以与数据库中的表一一对应的字段我们可以直接继承该类,简化代码

对于不是表对应的字段,我们则需要继承 serializers.Serializer

序列化

ModelSerializer

定义的序列化器,继承 serializers.ModelSerializer,在里面声明Meta类,通过指定对应的模型类和序列化字段即可完成最简单的序列化器编写。

对于单表查询这是非常快的,但是很多情况下我们都是需要在多个表之间进行联合查询。我们对model进行补充,设计外键进行约束,方便联合查询

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
class Student(models.Model):
SEX_OPTION = (
(0,'保密'),
(1,'男'),
(2,'女'),
)
name = models.CharField(max_length=20,verbose_name='姓名',help_text='姓名')
age = models.SmallIntegerField(verbose_name='年龄',help_text='年龄')
sex = models.SmallIntegerField(choices=SEX_OPTION,verbose_name='性别',help_text='性别,0->保密,1->男,2->女,')
classmate = models.CharField(db_column='classmate',max_length=13,verbose_name='班级',help_text='班级')
description = models.TextField(null=True,blank=True,verbose_name='个性签名',help_text='个性签名,可以为空')

class Meta:
db_table = 'db_student'
verbose_name = '学生信息'
verbose_name_plural = verbose_name

class Course(models.Model):
name = models.CharField(max_length=52,verbose_name='课程名称')
teacher = models.ForeignKey('Teacher',on_delete=models.DO_NOTHING,related_name='course',db_constraint=False)
class Meat:
db_stable = 'db_course'
def __str__(self):
return self.name

class Teacher(models.Model):
name = models.CharField(max_length=50,verbose_name='姓名')
sex = models.BooleanField(default=False)
class Meta:
db_table = 'db_teacher'
def __str__(self):
return self.name

class Achievement(models.Model):
score = models.DecimalField(default=0,max_digits=4,decimal_places=1,verbose_name='成绩')
student = models.ForeignKey(Student,on_delete=models.DO_NOTHING,related_name='s_achievement',db_constraint=False)
course = models.ForeignKey(Course,on_delete=models.DO_NOTHING,related_name='c_achievement',db_constraint=False)
create_time = models.DateTimeField(auto_created=datetime.now)
class Meta:
db_table = 'db_achievement'
def __str__(self):
return self.score

重新进行模型迁移和数据导入,写入几条测试数据

功能编写:

1
2
3
4
5
6
7
8
9
10
11
12
class CourseModelSerializer(serializers.ModelSerializer):
class Meta:
model = Course
fields = ['name']


# 第一种外键查 外面写个序列化器
class TeacherCourseModelSerializer(serializers.ModelSerializer):
course = CourseModelSerializer(many=True)
class Meta:
model = Teacher
fields = ['id', 'name', 'course']
1
2
3
4
5
6
7
8
9
def get_teacher_course(self,request):
'''
查询老师代课信息
:param request:
:return:
'''
teacher = Teacher.objects.all()
serializer = TeacherCourseModelSerializer(instance=teacher,many=True)
return Response(data=serializer.data,status=status.HTTP_200_OK)

这是第一种,需要外面写个序列化器,代码显得很冗余,第二种直接写里面,第三种是指明嵌套深度

1
2
3
4
5
6
7
8
9
10
class TeacherCourseModelSerializer(serializers.ModelSerializer):
# 第一种
# course = CourseModelSerializer(many=True)
# 第二种直接在里面写
# course =serializers.CharField(source='course.name')
class Meta:
model = Teacher
fields = ['id', 'name', 'course']
# 第三种 写个嵌套深度即可
depth = 1

第四种是通过外键指明深度,直接访问

1
2
3
4
5
6
7
# 第四种
class StudenAchievementModelSerializer(serializers.ModelSerializer):
class Meta:
model = Student
# s_achievement 外键名称
fields = ['id','name','s_achievement']
depth = 1

这四种实现的效果类似,都有层层嵌套,不好识别的问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class Student(models.Model):
SEX_OPTION = (
(0,'保密'),
(1,'男'),
(2,'女'),
)
name = models.CharField(max_length=20,verbose_name='姓名',help_text='姓名')
age = models.SmallIntegerField(verbose_name='年龄',help_text='年龄')
sex = models.SmallIntegerField(choices=SEX_OPTION,verbose_name='性别',help_text='性别,0->保密,1->男,2->女,')
classmate = models.CharField(db_column='classmate',max_length=13,verbose_name='班级',help_text='班级')
description = models.TextField(null=True,blank=True,verbose_name='个性签名',help_text='个性签名,可以为空')

class Meta:
db_table = 'db_student'
verbose_name = '学生信息'
verbose_name_plural = verbose_name

def __str__(self):
return self.name

@property
def achievement(self):
queryset = self.s_achievement.all()
ret = [{'score': item.score, 'course': item.course.name, 'teacher': item.course.teacher.name} for item in
queryset]
return ret
1
2
3
4
5
# 第五种方法 自定义属性 更加灵活 不像上面嵌套太多了 套娃
class StudentAchievement2ModelSerializer(serializers.ModelSerializer):
class Meta:
model = Student
fields = ['id','name','achievement']

反序列化

Serializer

Serializer的数据并不是来自数据库,所以我们在使用该类时需要自己去声明

1
2
3
4
5
6
7
8
9
10
11
# 声明序列化器,所有的序列化器都要直接或者间接继承于Serializer
# 其中,ModelSerializer是Serializer的子类,ModerlSerializer在Serializer的基础上进行了代码简化
class StudentSerializer(serializers.Serializer):
# 学生序列转换器
# 1.需要进行数据转换的字段
id = serializers.IntegerField(read_only=True) # 用户提交数据进行反序列化时,序列化器进行序列化转换数据给客户端时才转给客户端
name = serializers.CharField(max_length=20,required=True)
classmate = serializers.CharField(max_length=4,required=True)
age = serializers.IntegerField()
sex = serializers.IntegerField(validators= [check_sex,])
description = serializers.CharField(required=False,default='这家伙很懒,什么也没写!')

和ModelSerializer类似,因为ModelSerializer的数据来自数据库,所以直接与model关联即可,但Serializer的数据并不是来自数据库,需要手动声明。

在进行添加和更新数据操作时,ModelSerializer已经帮我们实现了create()和update()方法,但Serializer并没有实现,需要我们手动实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#4. 编写模型的添加和更新代码
# 添加操作 save() 要实现save
def create(self, validated_data):
# 添加数据的实现方法
student = Student.objects.create(**validated_data)
# 要是字段多了,不匹配,就要拿出来
# student = Student.objects.create(
# name = validated_data.get('name')
# .....
# )
# 返回添加后的模型
return student

# 更新操作要实现update()
def update(self, instance, validated_data):
'''
:param instance: 更新的模型对象数据
:param validated_data: 新数据
:return:
'''
if validated_data.get('name'):
instance.name = validated_data.get('name')
if validated_data.get('age'):
instance.age = validated_data.get('age')
if validated_data.get('sex'):
instance.sex = validated_data.get('sex')
if validated_data.get('classmate'):
instance.classmate = validated_data.get('classmate')
if validated_data.get('description'):
instance.description = validated_data.get('description')
instance.save()
return instance

数据校验

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 3.验证代码
# 3.1 对单个字段进行验证
# 格式: 方法名必须以‘valiidate_<字段名>’作为验证方法名,否则is_valid找不到
# 验证方法执行时,is_valid会把当前客户端提交的字段传递给验证方法,然后校验
# 验证方法把返回值回传给序列化,否则数据会丢失
def validate_name(self,data):
print(f'验证参数:{data}')
if data == 'Python':
# 抛异常
raise serializers.ValidationError(code='name',detail='当前输入存在敏感数据')
return data

# 3.2 对多个字段的数据进行验证
# 格式:一个序列化器只能有0or1个多字段校验,方法名位于
# 方法名必须叫 validate(self,data),否则is_valid找不到
# 步骤:接收参数 验证 返回
def validate(self, attrs):
age = attrs.get('age')
classmate = attrs.get('classmate')
if (age < 20) and str(classmate).startswith('5'):
raise serializers.ValidationError('对不起!年龄20以下不能加入到5字开头的班级中')
return attrs

做校验的数据是从客户端发来的,有的并不和数据库中字段一一对应,所以就不能用model声明的约束去控制了,需要手动判断。

序列化器的使用

当编写完序列化器后,可以在视图层是直接视图

更新和删除

1
2
3
4
student = Student.objects.get(pk=pk)
serializer = StudentModelSerializer(instance=student,data=params)
serializer.is_valid(raise_exception=True)
serializer.save()

这段是更新student的代码,通过接受的id去查询原信息,然后把原信息和客户端提交的信息放入序列化器,instance传入数据库查到的数据,data传入客户端提交的信息,is_valid进行参数校验,raise_exception=True是设置抛出异常,最后调用序列化器的save保存数据。

对于删除操作,很多时候并不是在数据库中把数据给删除掉,相反是修改IsDeleted的值,与更新操作类似

查询list和根据ID查询

1
2
teacher = Teacher.objects.all()
serializer = TeacherCourseModelSerializer(instance=teacher,many=True)

需要在放入序列化器的时候传入 many=True,内部会调用循环返回所有查询到的数据

根据ID查询某单个数据,不传 many即可,默认是 False

添加操作

1
2
3
4
param = request.data.dict()
serializer = TeacherModelSerializer(data=param)
serializer.is_valid(raise_exception=True)
serializer.save()