【问题标题】:Django Rest Framework Conditional Field on Serializer序列化器上的 Django Rest Framework 条件字段
【发布时间】: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 产生“条件字段”来实现这种效果?

【问题讨论】:

    标签: django django-rest-framework


    【解决方案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()
    

    【讨论】:

      猜你喜欢
      • 2015-12-19
      • 2013-07-07
      • 1970-01-01
      • 2020-06-09
      • 2018-06-07
      • 2015-07-01
      • 1970-01-01
      • 2015-02-19
      • 2019-07-20
      相关资源
      最近更新 更多