Django在请求完后,需要将返回的对象序列化成json串后,最后返回的时候才能以JsonRepsonse的形式返回出来,否则会报错,前端页面也需要根据返回的json串来做响应处理。
对象序列化
1. 继承serializers包下的ModelSeriralizer:
from rest_framework import serializersclass UserSerialize(serializers.ModelSerializer):class Meta:model = Userfields = ("id", "username", "password")
2. 在定义序列化的类时,一定要添加Meta,否则调用的时候会出错:
Class UserSerializers missing "Meta" attribute
3. 要序列化的字段为model里的字段,fields字段可以使用[]列表或者()元组来圈起来,也可以直接使用"__all__"来表示要序列化的所有字段, 实际开发中,只需要在fields里标注需要的字段。
class DeitalSerializer(serializers.ModelSerializer):class Meta:model = SysClassDetailfields = "__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 = CourseDetailfields = ('id', 'resource_id', 'image_path', 'hour', 'suit', 'goal', 'teacher', 'material')
在调用的时候,需要将对象传入到UserSerialize()当作参数,传进去,否则返回的就会只一个username:""
class UserLogin(APIView):@staticmethoddef s_result(result):r = _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).dataprint("序列化后的对象信息为:", r.data)## {'id': 4, 'username': 'zhuzhu', 'password': '123456'}request.session['user'] = r.dataprint('dict一些信息:', self.__dict__)except Exception as (e)return HttpResponse(e)return JsonResponse(r.data)
最后返回的时候,我们需要将序列化的json串r.data用JsonResponse包含返回出去。
在实际开发中,往往需要显示有些字段对应的中文意思给前端返回,那么就需要在序列化的时候,调用SerializerMethodField(),序列化方法,然后再根据英文来将中文对应起来即可。
需要注意的是:
1. 需要使用@staticmethod装饰器一个方法。
2. 装饰的方法由get_序列化的对象名。
3. 使用serializers.SerializerMethodField()来序列化。
import datetimefrom rest_framework import serializers# 绑定设备
from edu_dels import Studentclass StudentSerializers(serializers.ModelSerializer):# 序列化名称gender_name = serializers.SerializerMethodField()learn_goal_name = serializers.SerializerMethodField()education_name = serializers.SerializerMethodField()class Meta:model = Studentfields = ('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")@staticmethoddef get_gender_name(obj):gender = derif gender == 0:return "男"else:return "女"@staticmethoddef get_learn_goal_name(obj):goal = obj.learn_goalif goal == 0:return "一年目标"elif goal == 1:return "三到五年目标"elif goal == 2:return "长期目标"@staticmethoddef get_education_name(obj):edu = obj.educationif 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 = MyClassReourcefields = ('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 = SysClassDetailfields = "__all__"class ScheduleSerializer(serializers.ModelSerializer):class Meta:model = Schedulefields = "__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.dataclass Meta:model = SysClassfields = "__all__"
总结: 1. 对于单个表,可以直接通过"__all__"来直接序列化对应的model里的所有字段。
2. 如果表里包含外键的,可以直接调用外键的model里的序列化方法来执行,比如有个外键与student表进行关联,调用的序列化器必须要带括号,要不然序列化不生效:
class MyCommentSerializers(serializers.ModelSerializer):schedule = ScheduleSerializer()class Meta:model = Commentfields = "__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.dataclass Meta:model = SysClassfields = "__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 = VoucherRecordfields = ('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.bodybody_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. 反序列化的表中含有其他表单的外键。
本文发布于:2024-01-31 10:57:40,感谢您对本站的认可!
本文链接:https://www.4u4v.net/it/170666986228013.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
留言与评论(共有 0 条评论) |