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

Cur*_*984 24 django django-models django-rest-framework

我有两个模型,一个具有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)
Run Code Online (Sandbox Code Playgroud)

serializer.py:

class ToppingSerializer(serializers.ModelSerializer):
    class Meta:
        model = Topping
        fields = '__all__' 
Run Code Online (Sandbox Code Playgroud)

这有效,但不包括相关字段.

 fields = ['name', 'price', 'pizzas'] 
Run Code Online (Sandbox Code Playgroud)

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

fields = ['__all__', 'pizzas']
Run Code Online (Sandbox Code Playgroud)

此语法导致错误说:

字段名称__all__对于模型无效

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

hug*_*tti 29

就像@DanEEStart所说的那样,DjangoRestFramework没有一种简单的方法来扩展字段的' all '值,因为这些get_field_names方法似乎是按照这种方式设计的.

但幸运的是,您可以覆盖此方法,以允许一种简单的方法来包含所有字段和关系,而无需枚举大量字段.

我像这样重写这个方法:

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
Run Code Online (Sandbox Code Playgroud)

请注意,此方法仅更改此序列化程序的行为,并且该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']
Run Code Online (Sandbox Code Playgroud)

  • 我只是想让你知道你是一个救星,非常感谢你这个片段. (2认同)

Dan*_*tar 18

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

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

以下是相关源代码的片段:

    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__
        )
Run Code Online (Sandbox Code Playgroud)

您不能在元组或列表中添加" all "以及字段...

  • 伙计,这是一个基本功能,每次我研究 django 时,看起来一切都已损坏或一成不变,无法创建智能工厂或任何东西,只需为您想做的任何简单事情编写大量无用的代码。感谢你的回答 (3认同)

Aik*_*y30 12

一个老问题,但认为这可能在将来帮助其他人.

我刚刚遇到类似的问题并通过手动指定其他字段来完成" 全部 "选项,如下例所示.我不确定这是否也可以解决您的问题.这是一个比我见过的任何其他东西更干净的视线.

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__'
Run Code Online (Sandbox Code Playgroud)

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

我正在使用Django Rest Framework版本3.6.2

请求的反向关系示例:

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

    class Meta:
        model = Track
        fields = '__all__'
Run Code Online (Sandbox Code Playgroud)

  • 这应该被认为是正确的答案,因为它解决了框架提供的功能中的问题而没有额外的黑客攻击. (2认同)

Ele*_*wen 10

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

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

   def new_field_method(self, modelPointer_):
      return "MY VALUE"
Run Code Online (Sandbox Code Playgroud)

然后你仍然可以使用

class Meta:
   fields = '__all__'
Run Code Online (Sandbox Code Playgroud)


小智 8

嗨,我可以使用Django的_meta API达到预期的结果,该API自Django 1.11起似乎可用。所以在我的序列化器中,我做了:

model = MyModel
fields = [field.name for field in model._meta.fields]
fields.append('any_other_field')
Run Code Online (Sandbox Code Playgroud)

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

干杯!