序列化器上的 Django Rest Framework 条件字段

Django Rest Framework Conditional Field on Serializer

我有一个引用 Generic Relation 的模型,我想以详细的方式对其进行序列化。

class AType(models.Model):
    foo = CharField()


class BType(models.Model):
    bar = PositiveIntegerField()


class ToSerialize(models.Model):
    scope_limit = models.Q(app_label="app", model="atype") | \
                  models.Q(app_label="app", model="btype")
    content_type = models.ForeignKey(ContentType, limit_choices_to=scope_limit)
    object_id = models.PositiveIntegerField()
    content_object = generic.GenericForeignKey('content_type', 'object_id')

我希望 ToSerialize 视图集的列表方法的 JSON 看起来像:

[
    {
       "atype": { "id": 1, "foo": "a" }
    },
    {
       "atype": { "id": 2, "foo": "b" }
    },
    {
       "btype": { "id": 1, "bar": "1" }
    },
    {
       "btype": { "id": 2, "bar": "2" }
    }
]

有没有一种方法可以让 ToSerialize 对象的视图集的序列化程序根据 content_type/object_id 生成 "conditional fields" 来实现这种效果?

使用SerializeMethodField:

class YourSerializer(serializers.ModelSerializer):
    your_conditional_field = serializers.SerializerMethodField()

    class Meta:
        model = ToSerialize

    def get_your_conditional_field(self, obj):
        # do your conditional logic here
        # and return appropriate result
        return obj.content_type > obj.object_id

推荐的方法是创建自定义 RelatedField。检查 DRF docs about generic relationships 一个很好的例子。在 OP 案例中,它看起来像这样:

class ABTypeRelatedField(serializers.RelatedField):

    def to_representation(self, value):
        """
        Serialize objects to a simple textual representation.
        """
        if isinstance(value, AType):
            return 'AType: ' + value.foo
        elif isinstance(value, BType):
            return 'BType: ' + value.bar
        raise Exception('Unexpected type of content_object')


class ToSerializeSerializer(serializers.Serializer):
    content_object = ABTypeRelatedField()

None 个答案实际回答了问题。

最简单的做法是默认添加所有字段,然后根据您的情况在序列化程序初始化时将其删除。

在下面的示例中,我们不会在列出用户时返回电子邮件。

class UserSerializer():

    fields = ('username', 'email')

    class Meta:
        model = User

    def __init__(self, *args, **kwargs):

        # Don't return emails when listing users
        if kwargs['context']['view'].action == 'list':
            del self.fields['email']

        super().__init__(*args, **kwargs)