webentwicklung-frage-antwort-db.com.de

Django Rest-Framework, wie werden '__all__'-Felder und ein zugehöriges Feld in ModelSerializer aufgenommen?

Ich habe zwei Modelle, eines mit M2M-Beziehung und einem verwandten Namen. Ich möchte alle Felder in den Serializer und das zugehörige Feld aufnehmen.

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__' 

Dies funktioniert, schließt jedoch das zugehörige Feld nicht ein.

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

Das funktioniert genau so, wie ich es möchte, aber was passiert, wenn das Toppings-Modell viele Felder hat? Ich möchte etwas machen wie:

fields = ['__all__', 'pizzas']

Diese Syntax führt zu einem Fehler, der besagt:

Feldname __all__ gilt nicht für das Modell

Gibt es eine Möglichkeit, das gewünschte Verhalten zu erreichen? Oder müssen die Felder manuell eingegeben werden, wenn ein verwandter Name verwendet wird?

21
Curtwagner1984

Ich habe gerade den Quellcode von Django Rest Framework überprüft. Das gewünschte Verhalten scheint im Framework nicht unterstützt zu werden.

Die Option fields muss eine Liste, ein Tupel oder der Text __all__ Sein.

Hier ist ein Ausschnitt des relevanten Quellcodes:

    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__
        )

Sie können 'all' nicht zusätzlich zum Tupel oder zur Liste mit Feldern hinzufügen ...

17
DanEEStar

Wie @DanEEStart sagte, haben DjangoRestFramework keine einfache Möglichkeit, den Wert ' all ' für Felder zu erweitern, da die Methoden get_field_names scheint entworfen zu sein m so zu arbeiten .

Glücklicherweise können Sie diese Methode außer Kraft setzen, um auf einfache Weise alle Felder und Beziehungen einzuschließen, ohne Tonnen von Feldern aufzuzählen.

Ich überschreibe diese Methode wie folgt:

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

Beachten Sie, dass diese Methode nur das Verhalten dieses Serialisierers ändert und das Attribut extra_fields Nur für diese Serialisiererklasse funktioniert.

Wenn Sie über eine Menge solcher Serializer verfügen, können Sie eine Zwischenklasse erstellen, die diese get_fields_names - Methode an einem Ort einschließt und sie mehrmals wiederverwendet. Etwas wie das:

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']
25
hugoruscitti

Ein altes Problem, dachte aber, dass dies anderen in Zukunft helfen könnte.

Ich habe gerade ein ähnliches Problem festgestellt und festgestellt, dass die Option " all " funktioniert, indem ein zusätzliches Feld manuell wie im folgenden Beispiel angegeben wird. Ich bin mir nicht sicher, ob dies auch Ihr Problem lösen würde. Es ist ein verdammt sauberer Anblick als alles andere, was ich gesehen habe.

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__'

Ich gehe davon aus, dass dies für alle anderen verwandten Feldoptionen funktioniert, die auf derselben Seite aufgeführt sind: http://www.Django-rest-framework.org/api-guide/relations/#serializer-relations =

Ich verwende das Django Rest Framework Version 3.6.2

Reverse Relations Beispiel wie gewünscht:

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

    class Meta:
        model = Track
        fields = '__all__'
12
Aiky30

Hallo, ich könnte das erwartete Ergebnis erzielen, indem ich Djangos _meta API verwende, das anscheinend verfügbar ist seit Django 1.11. Also habe ich in meinem Serializer Folgendes getan:

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

In der Programmierung gibt es immer viele Möglichkeiten, das gleiche Ergebnis zu erzielen, aber diese oben hat wirklich für mich funktioniert.

Prost!

6
Wand

um alle Felder und die anderen in Ihrem Serializer definierten Felder einzuschließen, können Sie einfach exclude = () sagen.

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

Dies listet alle Feldwerte mit dem zusätzlichen Argument pizzas auf

2
riyasyash

So habe ich es gemacht, viel einfacher

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