需求:在使用django rest framework serializer在model 中定义的model时,当需要额外在serializer中增加一些model中没有的字段。
方法一修改数据库,利用model,在这就不详细说了。
方法二,不修改django的model,直接使用SerializerMethodField(method_name=None)
假设我们现在有一个Animal模型,其中有name,type,country字段,其中country是外键。在序列化Animal时,需要额外增加country的area信息
- class AnimalSerializer(serializers.ModelSerializer):
- country_area = serializers.SerializerMethodField()
-
- class Meta:
- model = Animal
- fields = ('id','name','type','country','country_area')
-
- def get_country_area(self,obj):
- return obj.country.area
SerializerMethodField是read-only字段,当不指定其method_name时,默认为get_field_name;如果使用ModelSerializer并指点字段时,要包含此时定义的字段。
补充知识:django restframework Serializer field
SerializerMethodField
这是一个只读字段。它通过调用它所连接的序列化类的方法来获得它的值。它可用于将任何类型的数据添加到对象的序列化表示中。
签名: SerializerMethodField(method_name=None)
method_name - 要调用序列化对象的方法的名称。如果不包含,则默认为 get_
由 method_name 参数引用的序列化方法应该接受一个参数(除了 self),这是要序列化的对象。它应该返回你想要包含在对象的序列化表示中的任何内容。例如:
场景介绍:一个用户往往对应多个角色,而角色字段并不在UserProfile表中,这就需要我们新增角色字段到user序列化中
- from .models import UserProfile
- from .models import UserRole
-
- class UserProfileSerializer(serializers.ModelSerializer):
- """
- show list serializer
- """
- role = serializers.SerializerMethodField()
-
- class Meta:
- model = UserProfile
- # fields = "__all__"
- fields = ["id", "username","role", "account", "really_name",
- "department", "tel_phone", "create_time", "email",
- "last_time", "creator"]
-
- def get_role(self,obj):
- user_id = obj.id
- roles = [i.role.name for i in UserRole.objects.filter(user_id=user_id)]
- roles = ",".join(roles)
- return roles
相反的场景:我们提交的表单数据存在于多表中(因为表中含有多对多字段),如何验证所有字段,并保存完整的数据到各表中。
刚开始的思路在ModelSerializer中新增未定义字段,然后发现这并不可行。我也犯了SerializerMethodField的错误,但随后去读了serializer的源码,以及了解serializer的顺序,就知道SerializerMethodField仅用于list方法。
问题的突破点到底在哪里呢,在drf serializer 官方文档中就有一个知识点,serializer.save(**kwargs),kwargs数据被绑定在serializer.validated_data对象上,当create或update的时候就会被添加进数据库,相当于validated_data.update(kwargs)
实际源码不是这样子,但也就是这么个意思。
重写Serializer create,update方法
- from rest_framework.utils import model_meta
-
- def create(self, validated_data):
- field= validated_data.pop('field_name')
- validated_data = validated_data
- instance = Model.objects.create(**validated_data)
- # ...外键表的操作
- return instance
-
- def update(self, instance, validated_data):
- field= validated_data.pop('field_name')
- info = model_meta.get_field_info(instance)
-
- for attr, value in validated_data.items():
- if attr in info.relations and info.relations[attr].to_many:
- field = getattr(instance, attr)
- field.set(value)
- else:
- setattr(instance, attr, value)
- instance.save()
- # ...
- return instance