Django Rest 框架,如何在 ModelSerializer 中包含“__all__”字段和相关字段?

Posted

技术标签:

【中文标题】Django Rest 框架,如何在 ModelSerializer 中包含“__all__”字段和相关字段?【英文标题】:Django Rest framework, how to include '__all__' fields and a related field in ModelSerializer ? 【发布时间】:2016-11-09 18:26:59 【问题描述】:

我有两个模型,一个具有 M2M 关系和一个相关名称。我想在序列化程序和相关字段中包含 所有 字段。

models.py:

class Pizza(models.Model):
    name = models.CharField(max_length=50, unique=True)
    toppings = models.ManyToManyField(Topping, null=True, blank=True, related_name='pizzas')

class Topping(models.Model):
    name = models.CharField(max_length=50, unique=True)
    price = models.IntegerField(default=0)

serializer.py:

class ToppingSerializer(serializers.ModelSerializer):
    class Meta:
        model = Topping
        fields = '__all__' 

这可行,但不包括相关字段。

 fields = ['name', 'price', 'pizzas'] 

这完全符合我的要求,但是当 Toppings 模型有很多字段时会发生什么。我想做类似的事情:

fields = ['__all__', 'pizzas']

此语法导致错误提示:

字段名称__all__ 对模型无效

有没有办法实现想要的行为?或者使用相关名称时必须手动输入字段?

【问题讨论】:

***.com/questions/14573102/… - 答案再好不过了 - 如果来自作者本人的话。 @karthikr 这并没有真正解释如何做我想做的事。它只是解释了如何嵌套 M2M。我不想那样做。我想在序列化程序“字段”中包含相关字段和 all 标记 【参考方案1】:

就像@DanEEStart 所说,DjangoRestFramework 没有简单的方法来扩展字段的“all”值,因为get_field_names 方法似乎设计为to work that way。

但幸运的是,您可以重写此方法,从而以一种简单的方式包含所有字段和关系,而无需枚举大量字段。

我这样重写这个方法:

class ToppingSerializer(serializers.ModelSerializer):

    class Meta:
        model = Topping
        fields = '__all__'
        extra_fields = ['pizzas']

    def get_field_names(self, declared_fields, info):
        expanded_fields = super(ToppingSerializer, self).get_field_names(declared_fields, info)

        if getattr(self.Meta, 'extra_fields', None):
            return expanded_fields + self.Meta.extra_fields
        else:
            return expanded_fields

注意这个方法只改变这个序列化器的行为,extra_fields 属性只对这个序列化器类起作用。

如果您有大量这样的序列化程序,您可以创建一个中间类,将这个get_fields_names 方法包含在一个地方并多次重复使用它们。有些是这样的:

class CustomSerializer(serializers.HyperlinkedModelSerializer):

    def get_field_names(self, declared_fields, info):
        expanded_fields = super(CustomSerializer, self).get_field_names(declared_fields, info)

        if getattr(self.Meta, 'extra_fields', None):
            return expanded_fields + self.Meta.extra_fields
        else:
            return expanded_fields


class ToppingSerializer(CustomSerializer):

    class Meta:
        model = Topping
        fields = '__all__'
        extra_fields = ['pizzas']

class AnotherSerializer(CustomSerializer):

    class Meta:
        model = Post
        fields = '__all__'
        extra_fields = ['comments']

【讨论】:

我只是想让你知道你是一个救星,非常感谢你的这个 sn-p。【参考方案2】:

我刚刚查看了 Django Rest Framework 的源代码。 框架似乎不支持您想要的行为。

fields 选项必须是列表、元组或文本 __all__

这里是相关源码的sn-p:

    ALL_FIELDS = '__all__'
    if fields and fields != ALL_FIELDS and not isinstance(fields, (list, tuple)):
        raise TypeError(
            'The `fields` option must be a list or tuple or "__all__". '
            'Got %s.' % type(fields).__name__
        )

您不能将“all”另外添加到带有字段的元组或列表中...

【讨论】:

伙计,这是一个非常基本的功能,每次我查看 django 时,似乎一切都被打破或一成不变,无法制造智能工厂或任何东西,只需编写大量无用的代码对于您想做的任何简单的事情。谢谢你的回答【参考方案3】:

fields="__all__" 选项可以通过按照以下示例手动指定附加字段来工作。对于这个问题,这是迄今为止最干净的解决方案。

嵌套关系

http://www.django-rest-framework.org/api-guide/relations/#nested-relationships

class TrackSerializer(serializers.ModelSerializer):
    class Meta:
        model = Track
        fields = '__all__'

class AlbumSerializer(serializers.ModelSerializer):
    tracks = TrackSerializer(many=True, read_only=True)

    class Meta:
        model = Album
        fields = '__all__'

我认为这适用于同一页面上列出的任何其他相关字段选项:http://www.django-rest-framework.org/api-guide/relations/#serializer-relations

反向关系示例

class TrackSerializer(serializers.ModelSerializer):
    album = AlbumSerializer(source='album_id')

    class Meta:
        model = Track
        fields = '__all__'

注意: 使用 Django Rest Framework 3.6.2 版创建,可能会发生变化。如果未来的任何更改破坏了上面发布的任何示例,请添加评论。

【讨论】:

这在反向关系的情况下不起作用 -> django-rest-framework.org/api-guide/relations/… @IshanKhare 更新以表明这也适用于反向关系。 这确实应该被接受为正确答案,因为它解决了框架提供的功能范围内的问题,而无需额外的黑客攻击。【参考方案4】:

嗨,我可以通过使用 Django's _meta API 来达到预期的结果,这似乎是从 Django 1.11 开始可用的。所以在我的序列化器中我做了:

model = MyModel
fields = [field.name for field in model._meta.fields]
fields.append('any_other_field')

在编程中,总是有很多方法可以达到相同的结果,但上面的这个对我真的很有效。

干杯!

【讨论】:

这在我看来效果最好。你也可以fields.extend(['field1', 'field2']) 这个好干净!【参考方案5】:

如果您尝试基本上只是将额外的信息添加到序列化对象中,则根本不需要更改字段部分。添加一个字段:

class MySerializer(serializers.ModelSerializer):
   ...
   new_field = serializers.SerializerMethodField('new_field_method')

   def new_field_method(self, modelPointer_):
      return "MY VALUE"

那么你仍然可以使用

class Meta:
   fields = '__all__'

【讨论】:

这应该是公认的答案。谢谢! 这是一个只读字段!【参考方案6】:

要包含序列化程序中定义的所有字段和其他字段,您只需说exclude = ()

class ToppingSerializer(serializers.HyperlinkedModelSerializer):
   pizzas = '<>' #the extra attribute value
    class Meta:
        model = Topping
        exclude = ()

这将列出所有带有额外参数比萨饼的字段值

【讨论】:

【参考方案7】:

这就是我的做法,更容易

class OperativeForm(forms.ModelForm):
    class Meta:
        model = Operative
        fields = '__all__'
        exclude = ('name','objective',)
        widgets = '__all__':'required'

【讨论】:

收到此错误:Cannot set both 'fields' and 'exclude' options on serializer

以上是关于Django Rest 框架,如何在 ModelSerializer 中包含“__all__”字段和相关字段?的主要内容,如果未能解决你的问题,请参考以下文章

django rest框架-如何在根级别停止子重复

如何在 django rest 框架中列出来自特定模型的所有对象?

如何更新 django rest 框架中的多个对象?

如何获取主键相关字段嵌套序列化器django rest框架的所有值

如何在 django rest 框架中获取与外键相关的数据?

如何在 django rest 框架中添加自定义权限和角色