Django的序列化与反序列化验证

一、一般序列化

       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. 反序列化的表中含有其他表单的外键。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

乌托邦钢铁侠

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值