一、一般序列化
Django在请求完后,需要将返回的对象序列化成json串后,最后返回的时候才能以JsonRepsonse的形式返回出来,否则会报错,前端页面也需要根据返回的json串来做响应处理。
对象序列化
1. 继承serializers包下的ModelSeriralizer:
from rest_framework import serializers
class UserSerialize(serializers.ModelSerializer):
class Meta:
model = User
fields = ("id", "username", "password")
2. 在定义序列化的类时,一定要添加Meta,否则调用的时候会出错:
Class UserSerializers missing "Meta" attribute
3. 要序列化的字段为model里的字段,fields字段可以使用[]列表或者()元组来圈起来,也可以直接使用"__all__"来表示要序列化的所有字段, 实际开发中,只需要在fields里标注需要的字段。
class DeitalSerializer(serializers.ModelSerializer):
class Meta:
model = SysClassDetail
fields = "__all__"
4 . 序列化多对多的情况 ManyToManyField
例如,file和HandsOnCaseItems为多对多的关系。
1)model代码
class CourseDetail(TimestampModel):
resource = models.ForeignKey(Resource, verbose_name="课程", to_field="id", related_name="course_detail_resource_id",
on_delete=models.DO_NOTHING)
suit = models.ManyToManyField(SuitableObject, verbose_name="适合")
goal = models.ManyToManyField(StudyGoal, verbose_name="学习目标")
hour = models.IntegerField(verbose_name="课时")
teacher = models.ManyToManyField(Teacher, verbose_name="老师")
material = models.ManyToManyField(TeacherMaterial, verbose_name="教材")
image_path = models.FileField(verbose_name="图文", upload_to=resource_upload_to, default=" ")
class Meta:
verbose_name = "课程详情"
verbose_name_plural = "课程详情"
db_table = "sys_course_detail"
2) 序列化代码 , 对ManyToMany的字段添加属性 many=True
class CourseDetailSerializers(serializers.ModelSerializer):
suit = SuitableObjectSerializers(many=True)
goal = StudyGoalSerializers(many=True)
teacher = TeacherSerializers(many=True)
material = TeacherMaterialSerializers(many=True)
image_path = serializers.CharField()
class Meta:
model = CourseDetail
fields = ('id', 'resource_id', 'image_path', 'hour', 'suit', 'goal', 'teacher', 'material')
在调用的时候,需要将对象传入到UserSerialize()当作参数,传进去,否则返回的就会只一个username:""
class UserLogin(APIView):
@staticmethod
def s_result(result):
r = result.to_json_string()
return JsonResponse(result.__dict__,
json_dumps_params={'sort_keys': False, 'indent': 4, 'ensure_ascii': False}, safe=False)
def post(self, request):
r = Result()
try:
username = request.data["username"]
password = request.data["password"]
result = User.objects.filter(username=username, password=password).first()
if result is None:
print("出错了!")
raise Exception("用户名或密码错误!")
# 否则登录成功, 在赋值给session之前需要先将result转换为json串,否则报错:TypeError: Object of type User is not JSON serializable
# request.session['user'] = result
# print(request.session['user'])
print("找到记录:", result)
#当前登录的result为User对象里的信息
# 序列化的时候此处必须要传result,否则序列化后的结果为""
r.data = UserSerialize(result).data
print("序列化后的对象信息为:", r.data)
## {'id': 4, 'username': 'zhuzhu', 'password': '123456'}
request.session['user'] = r.data
print('dict一些信息:', self.__dict__)
except Exception as e:
r.error(e)
return HttpResponse(e)
return JsonResponse(r.data)
最后返回的时候,我们需要将序列化的json串r.data用JsonResponse包含返回出去。
二、序列化显示英文字段对应的中文
在实际开发中,往往需要显示有些字段对应的中文意思给前端返回,那么就需要在序列化的时候,调用SerializerMethodField(),序列化方法,然后再根据英文来将中文对应起来即可。
需要注意的是:
1. 需要使用@staticmethod装饰器一个方法。
2. 装饰的方法由get_序列化的对象名。
3. 使用serializers.SerializerMethodField()来序列化。
import datetime
from rest_framework import serializers
# 绑定设备
from edu_saas_app.models import Student
class StudentSerializers(serializers.ModelSerializer):
# 序列化名称
gender_name = serializers.SerializerMethodField()
learn_goal_name = serializers.SerializerMethodField()
education_name = serializers.SerializerMethodField()
class Meta:
model = Student
fields = (
'id', 'gender', 'phone', 'enabled', 'description', 'name', 'create_datetime', 'avatar', 'real_name',
'gender',
'birthday', 'education', 'signature', 'learn_target', 'learn_goal', "address", "gender_name",
"learn_goal_name", "education_name")
@staticmethod
def get_gender_name(obj):
gender = obj.gender
if gender == 0:
return "男"
else:
return "女"
@staticmethod
def get_learn_goal_name(obj):
goal = obj.learn_goal
if goal == 0:
return "一年目标"
elif goal == 1:
return "三到五年目标"
elif goal == 2:
return "长期目标"
@staticmethod
def get_education_name(obj):
edu = obj.education
if edu == 0:
return "初中"
elif edu == 1:
return "高中"
elif edu == 2:
return "大专"
elif edu == 3:
return "本科"
elif edu == 4:
return "硕士"
elif edu == 5:
return "博士"
三、中间表序列化
在数据库中,往往会有两个库需要建立一个中间表,中间表存两个数据库关联字段的id,通过中间表来得到另外两张表的信息,只需要通过序列化的方式,不需要我们自己写sql。
原理其实很简单,通过中间表,序列化另外两个表。
model:
第一步,关联Student表和ReourcePackage表,得到student和rouce对象,在后续序列化的时候需要用到它:
class MyClassReource(TimestampModel):
student = models.ForeignKey(Student, verbose_name='资源', to_field='id',
related_name='student_id',
on_delete=models.DO_NOTHING, null=True)
rource = models.ForeignKey(ResourcePackage,to_field='id', related_name="resource_package_id",
on_delete=models.DO_NOTHING, null=True)
def __str__(self):
return "我的班级表"
class Meta:
verbose_name = "我的班级"
verbose_name_plural = "我的班级资源包"
db_table = "user_to_resource"
序列化:
第二步, 通过model定义的student和rource对象来获取Sudent表和ResourcePackage表里的字段信息:
class MyClassInfoSerializer(serializers.ModelSerializer):
user_id = serializers.IntegerField(source='student.id')
user_name = serializers.CharField(source='student.name', allow_blank=True, allow_null=True)
resource_package_id = serializers.CharField(source='rource.id', allow_blank=True, allow_null=True)
resource_name = serializers.CharField(source='rource.name', allow_blank=True, allow_null=True)
resource_price = serializers.CharField(source='rource.price', allow_blank=True, allow_null=True)
resource_sales = serializers.CharField(source='rource.sales', allow_blank=True, allow_null=True)
resource_description = serializers.CharField(source='rource.description', allow_blank=True,
allow_null=True)
class Meta:
model = MyClassReource
fields = ('user_id',
'user_name',
'resource_package_id',
'resource_name',
'resource_price',
'resource_sales',
'resource_description')
执行:
第三步,执行序列化,一定要带many=True属性,否则会报错:
Got AttributeError when attempting to get a value for field `user_id` on serializer `MyClassInfoSerializer`.
r.data = MyClassInfoSerializer(d, many=True).data
序列化完毕后,再转成json串后,再通过JSONRepsonse()返回出去。
return JsonResponse(result.__dict__,
json_dumps_params={'sort_keys': False, 'indent': 4, 'ensure_ascii': False}, safe=False)
一对多ForeignKey、多对多ManyToMany模式结合:
class DeitalSerializer(serializers.ModelSerializer):
class Meta:
model = SysClassDetail
fields = "__all__"
class ScheduleSerializer(serializers.ModelSerializer):
class Meta:
model = Schedule
fields = "__all__"
class MyClassInfoSerializer(serializers.ModelSerializer):
id = serializers.IntegerField()
name = serializers.CharField()
# 多对一
detail = DeitalSerializer()
# 多对多
schedule = serializers.SerializerMethodField()
def get_schedule(self, obj):
sch = obj.schedule.all()
s = ScheduleSerializer(sch, many=True)
return s.data
class Meta:
model = SysClass
fields = "__all__"
总结: 1. 对于单个表,可以直接通过"__all__"来直接序列化对应的model里的所有字段。
2. 如果表里包含外键的,可以直接调用外键的model里的序列化方法来执行,比如有个外键与student表进行关联,调用的序列化器必须要带括号,要不然序列化不生效:
class MyCommentSerializers(serializers.ModelSerializer):
schedule = ScheduleSerializer()
class Meta:
model = Comment
fields = "__all__"
3. 对于多对多模型的,可以先调用一下模型的序列化器,然后再根据获取到的所有对象来做一个继续序列化:
class MyClassInfoSerializer(serializers.ModelSerializer):
id = serializers.IntegerField()
name = serializers.CharField()
# 多对一
detail = DeitalSerializer()
# 多对多
schedule = serializers.SerializerMethodField()
# student = serializers.SerializerMethodField()
def get_schedule(self, obj):
sch = obj.schedule.all()
print("sch:", sch)
s = ScheduleSerializer(sch, many=True)
return s.data
# def get_student(self, obj):
# stu = obj.student.all()
# s = StudentSerializers(stu, many=True)
# return s.data
class Meta:
model = SysClass
fields = "__all__"
根据schedule的名字来直接在序列化器里定义方法的名称, 将里面序列化的对象进行再序列化后,把data返回:
def get_schedule(self, obj):
sch = obj.schedule.all()
print("sch:", sch)
s = ScheduleSerializer(sch, many=True)
return s.data
四、反序列化
在项目中,我们会遇到那种保存多行信息的情况,最普通的方式是: 遍历Body里的每一个{}, 然后将{} 里的字段一一取出来,然后赋值给model里面的属性字段。这种方式的优点是简单明了,但是当字段的数量比较多时,通过这种方式来保存效率会很低。
1. 通过反序列化可以将json数据映射成model,进而使用model来进行保存和修改, 下面是反序列化单表,没有关联关系:
序列化器:
class VoucherRecordSerializers(serializers.ModelSerializer):
class Meta:
model = VoucherRecord
fields = (
'id','type','subject')
body里的json为:
[
{
"id": 1,
"type": 3,
"subject":"应收现金"
},{
"id":4,
"type":0,
"subject":"应收人民币"
}
]
问题记录:
1) 如果在save和update前出现不允许访问数据库的情况,如以下报错:
You cannot call `.save()` after accessing `serializer.data`...................
解决方案,在save前需要做validate(),参数为attrs, attrs为传入的model对象里的属性序列,如上,传入了id, type , subjet,那么attrs=("id","type","subject")
排除is_valid()方法错误的方式, 传一下raiseException=True
yes = record.is_valid(raiseException=True)
2) 使用反序列化的对象调用update()方法时,需要传入两个参数:
def update(self, instance, validated_data):
instance: model对象
validated_data: 已经验证过的字典 ,结构是一个字典。
以下是反序列化的完整过程
student = self.auth(request)
body = request.body
body_content = eval(body)
for i in body_content:
record = VoucherRecordSerializers(data=i)
yes = record.is_valid()
if not yes:
raise Exception("反序列化出现错误!")
record_id = i["id"]
res = VoucherRecord.objects.filter(id=record_id, user_id=student["id"],
case_item_id=case_item_id).first()
attrs = ("id", "type", "subject")
record.validate(attrs)
if res is None:
record.save()
else:
record.update(res, i)
2. 反序列化的表中含有其他表单的外键。