Django Rest, как включить поля "__all__" и связанную область в ModelSerializer?

У меня две модели: одна с отношением M2M и связанное имя. Я хочу включить поля all в сериализаторе и связанном с ним поле.

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__ недействительно для модели

Есть ли способ достичь желаемого поведения? Или поля должны вводиться вручную при использовании связанного имени?

Ответ 1

Я только что проверил исходный код Django Rest Framework. Поведение, которое вы хотите, похоже, не поддерживается в 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__
        )

Вы не можете добавить ' all' дополнительно к кортежу или списку с полями...

Ответ 2

Как и @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

Обратите внимание, что этот метод изменяет поведение этого сериализатора, а атрибут 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']

Ответ 3

Старая проблема, но думал, что это может помочь другим в будущем.

Я только что столкнулся с подобной проблемой и получил опцию " все ", указав дополнительное поле вручную, как показано в следующем примере. Я не уверен, что это также решит вашу проблему. Это чертовски чище, чем все остальное, что я видел.

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

Я использую Django Rest Framework версии 3.6.2

Пример обратной связи по запросу:

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

    class Meta:
        model = Track
        fields = '__all__'

Ответ 4

Привет я мог достичь ожидаемого результата с помощью Django _meta API, который, кажется, доступен с Django 1.11. Так в моем сериализаторе я сделал:

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

В программировании всегда есть много способов достичь того же результата, но этот способ действительно сработал для меня.

Ура!

Ответ 5

чтобы включить все поля и другие поля, определенные в вашем сериализаторе, вы можете просто сказать exclude = ()

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

Здесь перечислены все значения полей с дополнительным аргументом pizzas

Ответ 6

Вот как я это сделал, намного проще

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