Django Rest Framework序列化器中to_representation的应用

1、嵌套序列化器中筛选列表数据
model
其中包含一个Project模型,另一个与之关联的Task模型

点击查看代码
class Project(models.Model):
    name = models.CharField(max_length=200)
    ...

class Task(models.Model):
    name = models.CharField(max_length=200)
    project = models.ForeignKey(Project, on_delete=models.CASCADE, null=True)
    is_active = models.BooleanField(default=False)
    ...
serialize
class TaskSerializers(serializers.ModelSerializer):
    class Meta:
        model = Task
        fields = ('id', 'name', 'is_active')


class ProjectSerializer(serializers.ModelSerializer):
    tasks = TaskSerializers(many=True)

    class Meta:
        model = Project
        fields = ('id', 'name', 'tasks')

问题: 在上面嵌套的序列化中,检索到的是Project对应的所有Task,即使有些Task的is_active=False,但是我们希望只检索is_active=True的。

解决方案: 重写ListSerializers 中的to_represent(),对queryset进行过滤,同时在TaskSerializers中使用list_serializer_class = ActiveTaskSerializer

点击查看代码
class ActiveTaskSerializer(serializers.ListSerializer):
    def to_representation(self, data):
        data = data.filter(is_active=True)
        super(ActiveTaskSerializer, self).to_representation(data)


class TaskSerializers(serializers.ModelSerializer):
    class Meta:
        model = Task
        fields = ('id', 'name', 'is_active')
        list_serializer_class = ActiveTaskSerializer

2、移除返回数据中空的字段
问题:

点击查看代码
{
    "meta": {
        "title": null,
        "name": "XYZ"
    }
}

// 希望去掉序列化结果中为None的字段

{
    "meta": {
        "name": "XYZ"
    }
}

解决方案: 定义NonNullModelSerializer,使序列化继承NonNullModelSerializer**

点击查看代码
class NonNullModelSerializer(serializers.ModelSerializer):
    def to_representation(self, instance):
        result = super().to_representation(instance)
        return OrderedDict(filter(lambda x: x[1] is not None, result.items()))

class ShipmentResponseSerializer(NonNullModelSerializer):
    ....

3、ChoiceField

点击查看代码
class Project(models.Model):
    TYPE = [
        (1, 'Person'),
        (2, 'Team'),
    ]
    type = models.PositiveSmallIntegerField(max_length=1, choices=TYPE, default=1)
    name = models.CharField(max_length=200)
    

class ProjectSerializer(serializers.ModelSerializer):
    type = serializers.CharField(source='get_type_display', read_only=True)

    class Meta:
        model = Project
        fields = ['id', 'name', 'type']
上面序列化器我们可以使type字段显示值而不是数字

c = Project.objects.first()
ser = ProjectSerializer(instance=c)
print(ser.data)

{  'id': 1, 
  'name': 'xxxx', 
  'type': 'Person',
   # instead of 'type': '1'
}
问题: 但是对于POST / PUT,则会报错,并且希望在创建时type传递的是值而不是数字

data = {
    'type': 'Team',
    # instead of 'type': '2'
    'name': 'xxx'
}
ser2 = ProjectSerializer(data=data)
ser2.is_valid(raise_exception=True)
instance = ser2.save()

# 错误
TypeError: Project() got an unexpected keyword argument 'get_type_display'
解决方案:

class MappedChoiceField(serializers.ChoiceField):

    def to_representation(self, obj):
        if obj == '' and self.allow_blank:
            return ''
        return self._choices[obj]

    def to_internal_value(self, data):
        # To support inserts with the value
        if data == '' and self.allow_blank:
            return ''

        for key, val in self._choices.items():
            if val == data:
                return key
        self.fail('invalid_choice', input=data)

class ProjectSerializer(serializers.ModelSerializer):
    # type = serializers.CharField(source='get_type_display')
    type = MappedChoiceField(choices=[
        (1, 'Person'),
        (2, 'Team'),
    ])

    class Meta:
        model = Project
        fields = ['id', 'name', 'type']

#1、 获取数据
c = Project.objects.first()
ser = ProjectSerializer(instance=c)
print(ser.data)
#  {'id': 1, 'name': 'xxxx', 'type': 'Person'}

# 2、创建数据
data = {
    'type': 'Team',
    'name': 'xxx'
}
ser2 = ProjectSerializer(data=data)
ser2.is_valid(raise_exception=True)
instance = ser2.save()
print(instance)
# Project object (75)

posted @ 2022-03-24 10:18  bkylww  阅读(172)  评论(0)    收藏  举报