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

827 阅读2分钟

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[1is 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)

image.png