Wie füge ich verwandte Modellfelder mit Django Rest Framework hinzu?

152

Nehmen wir an, wir haben das folgende Modell:

class Classroom(models.Model):
    room_number = [....]

class Teacher(models.Model):
    name = [...]
    tenure = [...]
    classroom = models.ForeignKey(Classroom)

Nehmen wir an, anstatt ein solches Ergebnis mit der Funktion ManyRelatedPrimaryKeyField zu erhalten:

{
    "room_number": "42", 
    "teachers": [
        27, 
        24, 
        7
    ]
},

Lassen Sie es etwas zurückgeben, das die vollständige zugehörige Modelldarstellung enthält, wie:

{
    "room_number": "42", 
    "teachers": [
        {
           'id':'27,
           'name':'John',
           'tenure':True
        }, 
        {
           'id':'24,
           'name':'Sally',
           'tenure':False
        }, 
    ]
},

Ist das möglich? Wenn das so ist, wie? Und ist das eine schlechte Idee?

Chaz
quelle

Antworten:

242

Am einfachsten ist es, das Tiefenargument zu verwenden

class ClassroomSerializer(serializers.ModelSerializer):
    class Meta:
        model = Classroom
        depth = 1

Dies schließt jedoch nur Beziehungen für Vorwärtsbeziehungen ein, was in diesem Fall nicht ganz das ist, was Sie benötigen, da das Lehrerfeld eine umgekehrte Beziehung ist.

Wenn Sie komplexere Anforderungen haben (z. B. umgekehrte Beziehungen einschließen, einige Felder verschachteln, andere jedoch nicht oder nur eine bestimmte Teilmenge von Feldern verschachteln ), können Sie Serialisierer verschachteln , z.

class TeacherSerializer(serializers.ModelSerializer):
    class Meta:
        model = Teacher
        fields = ('id', 'name', 'tenure')

class ClassroomSerializer(serializers.ModelSerializer):
    teachers = TeacherSerializer(source='teacher_set')

    class Meta:
        model = Classroom

Beachten Sie, dass wir das Quellargument im Serializer-Feld verwenden, um das Attribut anzugeben, das als Quelle des Felds verwendet werden soll. Wir könnten das sourceArgument löschen,teachers indem wir stattdessen sicherstellen, dass das Attribut vorhanden ist, indem wir die Option related_name in Ihrem TeacherModell verwenden, d. H.classroom = models.ForeignKey(Classroom, related_name='teachers')

Beachten Sie, dass verschachtelte Serializer derzeit keine Schreibvorgänge unterstützen. Für beschreibbare Darstellungen sollten Sie reguläre flache Darstellungen wie pk oder Hyperlinking verwenden.

Tom Christie
quelle
Als ich die erste Lösung ausprobierte, erhielt ich keine Lehrer, jedoch Instanzen des Elternteils des Klassenzimmers (was in diesem Beispiel nicht gezeigt wird). In der zweiten Lösung wurde eine Fehlermeldung angezeigt: "Das Objekt 'Klassenzimmer' hat kein Attribut 'Lehrer'". Vermisse ich etwas
Chaz
1
@Chaz Die Antwort wurde aktualisiert, um zu erklären, warum depthin diesem Fall nicht das getan wird, was Sie benötigen, und um zu erklären, welche Ausnahme Sie sehen und wie Sie damit umgehen.
Tom Christie
1
Ich bin ein Idiot und habe den falschen Server getroffen. Es funktioniert definitiv in vielen bis vielen Beziehungen.
Yellottyellott
15
Das Verschachteln von Serialisierern ist fantastisch! Ich musste dies tun und verwendete DRF 3.1.0. Ich musste umfassen many=Truewie so ...TeacherSerializer(source='teacher_set', many=True). Ansonsten bekam ich folgenden Fehler:The serializer field might be named incorrectly and not match any attribute or key on the 'RelatedManager' instance. Original exception text was: 'RelatedManager' object has no attribute 'type'.
Karthic Raghupathi
2
Die Rückseite eines ForeignKey wird ..._setstandardmäßig benannt. Weitere Informationen finden Sie in den Django-Dokumenten: docs.djangoproject.com/de/1.10/ref/models/relations/…
Tom Christie
35

Danke @TomChristie !!! Sie haben mir sehr geholfen! Ich möchte das ein wenig aktualisieren (wegen eines Fehlers, auf den ich gestoßen bin)

class TeacherSerializer(serializers.ModelSerializer):
    class Meta:
        model = Teacher
        fields = ('id', 'name', 'tenure')

class ClassroomSerializer(serializers.ModelSerializer):
    teachers = TeacherSerializer(source='teacher_set', many=True)

    class Meta:
        model = Classroom
        field = ("teachers",)
Eliyahu Tauber
quelle
2

Dies kann auch erreicht werden, indem ein ziemlich praktischer Dandy-Django verwendet wird, der als drf-flex-Felder bezeichnet wird . Wir benutzen es und es ist ziemlich großartig. Sie installieren es einfach pip install drf-flex-fields, führen es durch Ihren Serializer, fügen es hinzu expandable_fieldsund Bingo (Beispiel unten). Außerdem können Sie mithilfe der Punktnotation tief verschachtelte Beziehungen angeben.

from rest_flex_fields import FlexFieldsModelSerializer

class ClassroomSerializer(FlexFieldsModelSerializer):
    class Meta:
        model = Model
        fields = ("teacher_set",)
        expandable_fields = {"teacher_set": (TeacherSerializer, {"source": "teacher_set"})}

Dann fügen Sie ?expand=teacher_setIhrer URL hinzu und es wird eine erweiterte Antwort zurückgegeben. Hoffe, das hilft jemandem eines Tages. Prost!

Paul Tuckett
quelle