Django序列化的一些经验
序列化笔记
outline
ref:
序列化的方法写的很清楚
drf 官方解释
一、前言
问题引入
-
什么控制序列化、什么东西控制反序列化?
- 序列化器中的参数
read_only,write_only,如果不写,代表该字段及参加序列化又参加反序列化。还有Meta中的fields - get_字段名 自定义序列化的方法!见下面完整的例子
read_only代表仅仅进行序列化write_only代表仅仅代表反序列化
- 序列化器中的参数
-
序列化的字段与Django model字段有哪些联系与区别?
- 完全仿照model的字段,
- 区别,一个针对数据库,另一个针对反序列化时输入数据的格式
-
序列化器中Class Meta 字段中的Fiel字字段的作用是?
指定需要序列化和反序列化的字段 -
序列化器中仅仅写Fields字段,而不在类中写
name = serializer.CharField(max_length=100, min_length=)会怎么样?为什么不在FileNOdeSerializer里写name字段的具体格式
调类模型Model 默认的def __str__(self)方法,所以基本类型的字段会被展示;针对外键、多对多联系和时间等需要自行定义逻辑的就需要单独定义字段 -
序列的使用用
-
发现一个问题,使用update()更新数据时需要把动 各个字段取出来再写入ORM的实例,instance.filed = input.get("filed", None),这导致字段修改后每次手动调整update(),自己增加字段,太不智能了,要想create()一样就行。
总结:
- 字段不需要特殊处理,直接写在Meta.fields里
- Meta.fields是字段清单,决定哪些字段会被处理
- 单独定义的字段(如upload_time)是对特殊处理字段进行定制
- 一个model可以被写多个不同的序列化器
- 如何区分哪些字段是反序列化要用到的,哪些是序列化要用到的?
- 序列化(Model --> basic info) :所有在Meta.fields里的字段都会参加序列化(除非被标记为
write_only = True) - 反序列化(basic info --> Model) :所有在Meta.fiedls里的字段都会参加反序列化(除非被标记为
read_only = True) - 普通字段默认为既可以序列化又可以反序列化,因为默认
write_only = False而且read_only = False
- 序列化(Model --> basic info) :所有在Meta.fields里的字段都会参加序列化(除非被标记为
序列化类不仅能序列化模型中某个字段,还能序列化模型类中的方法
在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(),代表利用该序列化器对字段进行序列化 -
指定只读字段
-
序列化字段
- Boole field
- string field
- numeric field
- data and time fields
- choice selection fields
- File upload fields
- composite fields
- blablabla
详见: https://www.django-rest-framework.org/api-guide/fields/#listfield
-
不同的字段,允许为空的参数不同!
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 instancedemand_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=True 或 default,则使用该序列化器时默认值为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()
前言的回答
- 每次模型修字段后在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

浙公网安备 33010602011771号