Django序列化的一些经验

序列化笔记

outline
ref:
序列化的方法写的很清楚
drf 官方解释

一、前言

问题引入

  1. 什么控制序列化、什么东西控制反序列化?

    • 序列化器中的参数read_only,write_only,如果不写,代表该字段及参加序列化又参加反序列化。还有Meta 中的 fields
    • get_字段名 自定义序列化的方法!见下面完整的例子
    • read_only 代表仅仅进行序列化
    • write_only 代表仅仅代表反序列化
  2. 序列化的字段与Django model字段有哪些联系与区别?

    • 完全仿照model的字段,
    • 区别,一个针对数据库,另一个针对反序列化时输入数据的格式
  3. 序列化器中Class Meta 字段中的Fiel字字段的作用是?
    指定需要序列化和反序列化的字段

  4. 序列化器中仅仅写Fields字段,而不在类中写 name = serializer.CharField(max_length=100, min_length=) 会怎么样?为什么不在FileNOdeSerializer里写name字段的具体格式
    调类模型Model 默认的 def __str__(self) 方法,所以基本类型的字段会被展示;针对外键、多对多联系和时间等需要自行定义逻辑的就需要单独定义字段

  5. 序列的使用用

  6. 发现一个问题,使用update()更新数据时需要把动 各个字段取出来再写入ORM的实例,instance.filed = input.get("filed", None),这导致字段修改后每次手动调整update(),自己增加字段,太不智能了,要想create()一样就行。

总结:

  • 字段不需要特殊处理,直接写在Meta.fields里
  • Meta.fields是字段清单,决定哪些字段会被处理
  • 单独定义的字段(如upload_time)是对特殊处理字段进行定制
  1. 一个model可以被写多个不同的序列化器
  2. 如何区分哪些字段是反序列化要用到的,哪些是序列化要用到的?
    • 序列化(Model --> basic info) :所有在Meta.fields里的字段都会参加序列化(除非被标记为write_only = True
    • 反序列化(basic info --> Model) :所有在Meta.fiedls里的字段都会参加反序列化(除非被标记为 read_only = True)
    • 普通字段默认为既可以序列化又可以反序列化,因为默认write_only = False 而且read_only = False

序列化类不仅能序列化模型中某个字段,还能序列化模型类中的方法
在model中定义一个方法:
app.model

# 类模型中
class Book(models.Model):
    name = models.CharField(max_length=32)
    price = models.DecimalField(max_digits=5, decimal_places=2)
    publish_date = models.DateField(null=True)

    publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE)
    authors = models.ManyToManyField(to='Author')

    # 写了个方法,可以包装成数据属性,也可以不包
    def publish_detail(self):
        return {'name': self.publish.name, 'city': self.publish.city, 'email': self.publish.email}

    def author_list(self):
        res_list = []
        for author in self.authors.all():
            res_list.append({'id': author.id, 'name': author.name, 'age': author.age})
        return res_list
# 序列化类中
class BookSerializer(serializers.Serializer):
    name = serializers.CharField(max_length=8, min_length=3)
    price = serializers.IntegerField(min_value=10, max_value=99)
    publish_date = serializers.DateField()

    # 方式二:在表模型中写方法
    publish_detail = serializers.DictField(read_only=True)
    # 练习,使用方式二实现,显示所有作者
    author_list = serializers.ListField(read_only=True)

二、 应用场景

1. 输出指定格式 ---序列化
2. 读取数据创建实例 ---反序列化。
    需要在序列化器中定义字段说名格式的类型!!!   
  • 序列化器最开始是序列化python中的类,然后general,处理model。序列化和反序列话得弄清楚

  • 显示具体说明字段
    可以在序列化器添加额外的字段,或者重载默认字段通过在类内申明字段

        class AccountSerializer(serializers.ModelSerializer):
            url = serializers.CharField(source = 'get_absolute_url', read_only=True)
            groups = serializers.PrimaryKeyRelatedField(many = True)
    
            class Meta:
                model = Account
                fields = ['url', 'group']
    

    fields = ['url', 'group'] 代表向外序列化的字段有哪些
    url = serializers.CharField(source = 'get_absolute_url', read_only=True)
    groups = serializers.PrimaryKeyRelatedField(many = True) 这两行规定了给我反序列化中需要的数据格式!
    这两行所定义了序列化和反序列化的字段,序列化器类和django的表格很像,提供了类似的验证标签,比如 required, max_length, default
    如果在序列化器中写了这个字段user = UserSerializer(),代表利用该序列化器对字段进行序列化

  • 指定只读字段

  • 序列化字段

  • 不同的字段,允许为空的参数不同!

    friend_competitors_names = serializers.CharField(write_only=False, allow_blank=True)
    cell_capacity_min = serializers.IntegerField(allow_null=True)  
    
  • 保存模型实例
    如果我们想要返回基于验证后的数据转换成的模型实例,我们需要重写.create().update()两个方法

     def create(self, validated_data):
        user_name = validated_data.pop('username', None)
        if user_name:
            user_obj = User.objects.get(username = username)
        instance = Comment.objects.create(**validated_data)
        instance.sponsor = user_obj
        instance.save()
        return instance
    
    def update(self, instance, validated_data):
        instance.email = validated_data.get('email', instance.email)
        instance.content = validated_data.get('content', instance.content)
        instance.created = validated_data.get('created', instance.created)
        instance.save()
        return instance
    
    demand_tech_obj = Demand_tech_byKeyrelationSerializer(data = data)
    if demand_tech_obj.is_valid():
        demand_tech_obj.save()
    

    上面代码中,反序列化后获得一个序列化的对象,但是我们没有保存,只有调用.save(),它会根据参数情况选择序列化器中的create或者update函数.

    一定一定要记得,在create函数中如果修改了Instance必须手动调用一次instance.save()

序列化,把文件模型serializer

class FileNodeSerializer(serializers.ModelSerializer):
    children = serializers.SerializerMethodField()

    user_upload = serializers.SerializerMethodField()

    sub_admin=serializers.SerializerMethodField()

    upload_time = serializers.DateTimeField(format="%Y-%m-%d %H:%M:%S", required=False, read_only=True)
    class Meta:
        model = FileNode
        # fields = ['id', 'name','path', 'is_directory', 'parent', 'user_upload',
        #           'upload_time', 'description', 'children']
        fields = [ 'name','path', 'is_directory',  'user_upload',
                  'upload_time', 'description', 'children', 'sub_admin']
        
    def get_children(self, obj):
        # 如果当前节点是目录,则获取其子节点
        if obj.is_directory:
            children = obj.children.all()
            # 使用递归序列化器来序列化子节点
            serializer = FileNodeSerializer(children, many=True)  # 把children里多个child分别序列化,最后放到一个列表里
            return serializer.data
        return None
    
    def get_user_upload(self, obj):
        if obj.user_upload:
            user_obj = obj.user_upload
            first_id = user_obj.first_name + '('+ user_obj.username +  ')'
            return first_id
        return None
    
    def get_sub_admin(self, obj):
        # return [{'username':user.username, 'name':user.first_name} for user in obj.sub_admin.all()]
        return [user.first_name for user in obj.sub_admin.all()]

兼顾序列化和反序列化的方法来啦

  • 方法1.使用额外的新字段 technique_tags_ser = serializers.SerializerMethodField()
    在Meta 的fields里加入该字段technique_tags_ser,并在serializer里写如下方法

    def get_technique_tags_ser(self, obj):
          return [tag.name for tag in obj.technique_tags.all()]
    
  • 方法2. 使用to_representation (推荐),在序列化器中书写从而得到对应的表达式,无需使用额外的新字段!

    def to_representation(self, instance):
          rep =  super().to_representation(instance)
          
          tech_tags_colletction = []
          for it in instance.technique_tags.all():
              tech_tags_colletction.append( it.name )
          rep['technique_tags'] = tech_tags_colletction
          return rep
    

总结
不懂when 使用 . when 使用 '[tech_tags]', 忘记了访问多对多字段的方法:for it in instance.tech_tas.all()
完整的例子:

class Demand_describ_byKeyrelationSerializer(serializers.ModelSerializer):
  '''
  需求序列化器
  '''
  
 
  type = serializers.CharField( max_length=600 , allow_blank = True, required = False)
  
  time = serializers.DateField()

  # 是/否
  is_safe = serializers.BooleanField(required = False, allow_null=True)
  # 默认 required=False
  cell_capacity_typical = serializers.IntegerField(allow_null=True)
  cell_capacity_min = serializers.IntegerField(allow_null=True)
  
  # Foreign Key  4 rows
  product_type = serializers.CharField(write_only=False)
  sponsor = serializers.CharField(write_only=False)
  
  # many2many   only one row
  technique_tags = serializers.ListField(
      child=serializers.CharField(),
      # child=serializers.DictField(child=serializers.CharField()),
      required=False,
      write_only=True
  )

  # serializer
  technique_tags_ser = serializers.SerializerMethodField()


  class Meta:
      model = Demand_describ_byKeyrelation
      fields =  [ 'cell_type', ...]
              
  
  def get_technique_tags_ser(self, obj):
      return [tag.name for tag in obj.technique_tags.all()]

  def create(self, validated_data):
      
      #  foreign Key 预处理
      

      product_type_data   = validated_data.pop('product_type',None)
      sponsor_name        = validated_data.pop('sponsor',None)
     
      customer_name_data = validated_data.pop('customer_name',None)     

      # 1.2 collect ManyToMany,
      technique_tags_data = validated_data.pop('technique_tags',None)     
      #
      # ForeiKey changed from string to primary key number(int) in previous VIEW.PY,
      # So it can be treated as Int

      # 2. create instance of Demand_describ_byKeyrelation
      # rever-serializers!!!  grace!!!
      # for key,val in validated_data.item():
      #     print(f" {key}: {val} ")
      instace_of_Demand_describ_byKeyrelation = Demand_describ_byKeyrelation.objects.create(**validated_data)

      try:
          # hangdle ForeignKey
          if product_type_data:
              product_type_obj = Product_type.objects.get(name = product_type_data)
              instace_of_Demand_describ_byKeyrelation.product_type = product_type_obj
          if sponsor_name:
              sponsor_name_obj = User.objects.get(username = sponsor_name)
              instace_of_Demand_describ_byKeyrelation.sponsor = sponsor_name_obj
        
          if customer_name_data:
              customer_name_obj = Customer_Name.objects.get(name = customer_name_data)
              instace_of_Demand_describ_byKeyrelation.customer_name = customer_name_obj
      
          # raise Exception('')
          # todo how to raise Exception

      

      # handle ManytoMany
  

          for technique_tags_item in technique_tags_data:
              tag = Technique_tag.objects.get(name = technique_tags_item)
              # tag = Technique_tag.objects.get(name = technique_tags_item['name'])
              # tag, created = Technique_tag.objects.get_or_create(name = technique_tags_item['name'])
              instace_of_Demand_describ_byKeyrelation.technique_tags.add(tag)

          instace_of_Demand_describ_byKeyrelation.save()
      except Exception as e:
          print("Error in Serializer of Demand_describ_byKeyrelationSerializer")
          raise

      return instace_of_Demand_describ_byKeyrelation
  
  def update(self, instance, validated_data):
      # todo  to test
      # 1.1 pop  redundant key 
      # delete 'friend_competitors_names'
      validated_data.pop('friend_competitors_names') 

      instance.actually_max_incharge_rate = validated_data.get('actually_max_incharge_rate')
      instance.charging_cut_off_voltage = validated_data.get('charging_cut_off_voltage')
      instance.capacity_typical = validated_data.get('capacity_typical')
      instance.ED_min = validated_data.get('ED_min')
      instance.cathode_Si_content_min = validated_data.get('cathode_Si_content_min')
      instance.cathode_Si_content_max = validated_data.get('cathode_Si_content_max')
      instance.addtion_demand = validated_data.get('addtion_demand')
      instance.time_of_presentation = validated_data.get('time_of_presentation')
      instance.time_of_achievement = validated_data.get('time_of_achievement')
      instance.main_problem_point = validated_data.get('main_problem_point')


      # todo Foreignkey

      # todo ManytoManyto key
      
      instance.save()
      return instance
  
  def to_representation(self, instance):
      rep =  super().to_representation(instance)
      
      tech_tags_colletction = []
      for it in instance.technique_tags.all():
          tech_tags_colletction.append( it.name )
      rep['technique_tags'] = tech_tags_colletction
      return rep

反序列化器的介绍

voc_type = serializers.CharField(write_only = False, required = True) # 要求该字段非空

反序列化器字段介绍

总览

  • 外键使用 serializer.CharField
  • ManyToMany使用
  serializer.ListField(
                        child=serializer.charField(),
                        required = False
                        write_only=True)`

serializers.CharField
与models.CharField类似 ,可以用到外键联系,通过name找到对应的实例

cell_type = serializers.CharField( max_length=600 , allow_blank = True, required = False)

serializers.IntegerField
ED_min = serializers.IntegerField( allow_null = True, required = False)


serializers.DecimalField

cathode_Si_content_max = serializers.DecimalField( 
                    max_digits=6, 
                    decimal_places=4, 
                    allow_null = True, 
                    required = False)

serializers.DateField
time_of_presentation = serializers.DateField()


serializers.BooleanField
举例
is_safe = serializers.BooleanField(required = False, allow_null=True)


serializers.ListField
针对多对多的关系,比如多个标签,让这些标签组成一个数组

product_type = serializers.ListField(
    child=serializers.CharField(),
    required=False,
    write_only=True
)`

反序列化器参数介绍

read_only
默认为False,若设为True代表该字段只在把模型字段序列化输出!不能用在模型创建或更新一个实例

write_only
默认为False,如果为True,则代表只能读入数据到实例!(反序列化)
required
默认为True,代表反序列化时输入的数据必须包含该字段.
设置为False代表反序列化时不需要该字段,同时也允许在序列化输出时该字段被忽略
如果在模型model中有null=True 或者 blank=Truedefault,则使用该序列化器时默认值为False
allow_null
默认False,该字段的值是否可以为None。

反序列化时出错的信息查看

demand_obj = Demand_byKeyrelationSerializer(data = data)
if demand_obj.is_valid():
    demand_obj.save()
    print('demand_obj is valid')
    return { 'status':'success', 'message':'写入数据库成功!' }
else:
    # print(demand_tech_obj.errors)
    for key, val in demand_obj.errors.items():
        print(f"{key}{val}")
    print('obj isn\'t valid')
    return {'status': 'error', 'message': '前端发送的数据存在问题!'}

obj.errors 查看出错提示

序列化器的使用

user = User(name='test', age=22)

# 将user对象序列化为:{'name': 'test', 'age': 22}
serializer = UserSerializer(user)

# 获取序列化之后的数据
serializerData = serializer.data
print(serializerData)

反序化器的使用

demand_id = data['id']
demand_obj = Demand_byKeyrelation.objects.get(id = demand_id)
new_demand_obj = Demand_byKeyrelationSerializer(instance = demand_obj, data = data)
if new_demand_obj.is_valid():
    new_demand_obj.save()
    logger.info('========= 更新的需求数据没问题!!')
    return { 'status':'success', 'message':'修改需求成功!' }
else:
    # print(demand_tech_obj.errors)
    for key, val in new_demand_obj.errors.items():
        print(f"{key}{val}")
    print('obj isn\'t valid')
    return {'status': 'error', 'message': '前端发送的数据存在问题!'}

总结

调用序列化器之后,并且调用ser.is_valid()之后调用save()会根据传入的参数选择调用create() 还是 update()

前言的回答

  1. 每次模型修字段后在update()里手动调用字段
    你当前的update方法需要手动赋值所有字段,是因为完全重写了update逻辑,没有利用ModelSerializer内置的字段处理能力。ModelSerializer的父类BaseSerializer已经实现了默认的update方法,其核心逻辑是:
    这个默认逻辑会自动处理所有普通模型字段(非关系型字段,如CharField、IntegerField等),无需手动逐个赋值。我们只需要在子类中处理外键(ForeignKey)、多对多(ManyToManyField) 等需要额外处理的关系型字段即可。
    优化的实现:
class Demand_byKeyrelationSerializer(serializers.ModelSerializer):
    # (字段定义部分保持不变,省略)

    def update(self, instance, validated_data):
        # 1. 先提取需要特殊处理的字段(外键、多对多),避免父类默认逻辑错误处理
        # 外键字段
 
      
        customer_name_data = validated_data.pop('customer_name', None)
 
        
        # 多对多字段
        technique_tags_data = validated_data.pop('technique_tags', None)
        product_type_data = validated_data.pop('product_type', None)
        receivers_data = validated_data.pop('receivers', None)

        # 2. 调用父类默认update方法,自动处理所有普通字段(无需手动赋值)
        instance = super().update(instance, validated_data)

        # 3. 单独处理外键字段(特殊逻辑)
        try:
          

    

            if customer_name_data:
                customer_name_obj, _ = Customer_Name.objects.get_or_create(name=customer_name_data)
                instance.customer_name = customer_name_obj



            # 4. 单独处理多对多字段(先清空再重新添加)
            if product_type_data:
                instance.product_type.clear()
                for pro_type in product_type_data:
                    product_type_obj = Product_type.objects.get(name=pro_type)
                    instance.product_type.add(product_type_obj)

            if receivers_data:
                instance.receivers.clear()
                for receiver in receivers_data:
                    user_obj = User.objects.get(staff_id=receiver)
                    instance.receivers.add(user_obj)

            if technique_tags_data:
                instance.technique_tags.clear()
                for tag_item in technique_tags_data:
                    tag = Technique_tag.objects.get(name=tag_item)
                    instance.technique_tags.add(tag)

            # 5. 保存最终修改
            instance.save()
            logger.info('需求更新成功')

        except Exception as e:
            logger.error(f'更新外键/多对多关系失败: {str(e)}')
            raise

        return instance
posted @ 2025-09-01 10:38  朱义文有文化~  阅读(42)  评论(0)    收藏  举报