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

Posted

技术标签:

【中文标题】序列化器上的 Django Rest Framework 条件字段【英文标题】:Django Rest Framework Conditional Field on Serializer 【发布时间】:2015-01-14 16:32:09 【问题描述】:

我有一个引用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 视图集的 list 方法的 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 产生“条件字段”来实现这种效果?

【问题讨论】:

【参考方案1】:

没有一个答案能真正回答问题。

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

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

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)

【讨论】:

这不适用于在ListAPIViews 中使用的QuerySets。 @ErdinEray 可以尝试使用 ViewSetViewSets*View 很容易混合,但它们的界面却截然不同 抱歉,我无法编辑我的评论,所以我想添加一些其他内容。我想访问__init__ 上的instance 参数,因为我想根据模型实例上的某些属性有条件地删除一些字段。在这种情况下(访问instance),它在ListAPIView 中不起作用。您的示例访问context,这完全没问题,可以在ListAPIViews 中使用。 @ErdinEray 我对你的意思有点困惑。我的示例适用于视图集(尚未使用“api 视图”进行测试)。因此,只需确保您的类视图继承视图集而不是“api 视图”。它应该工作【参考方案2】:

使用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

【讨论】:

如果没有值,是否会从结果中“移除”条件字段? @GhislainLeveque, SerializeMethodField 是一个只读字段,这意味着它仅在将模型序列化为 JSON 时出现。从 JSON 反序列化到实例时,不能为其赋值。 @GhislainLeveque 不,它仍然会被包括在内。我不确定这个答案是否能解决问题。 @th0th,问题是根据某些条件要求包含该字段。 @th0th,是的。无论从函数返回什么都将在结果中。如果你想删除它,如果它是无,然后谷歌“DRF 动态序列化字段”。【参考方案3】:

推荐的方法是创建自定义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()

【讨论】:

以上是关于序列化器上的 Django Rest Framework 条件字段的主要内容,如果未能解决你的问题,请参考以下文章

序列化程序django rest框架上的字段值属性错误

Django Rest框架:序列化程序上的共享字段

Django REST framework 单元测试

django-rest-swagger 是不是不适用于模型序列化器?

Angular 2 前端 django 2 REST 框架后端用户身份验证

Django Rest 框架 ListField 和 DictField