Come posso includere campi modello correlati usando Django Rest Framework?


154

Diciamo che abbiamo il seguente modello:

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

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

Diciamo che invece di ottenere un risultato come questo per la funzione ManyRelatedPrimaryKeyField:

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

fare in modo che restituisca qualcosa che includa la rappresentazione completa del modello correlato come:

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

È possibile? Se é cosi, come? E questa è una cattiva idea?

Risposte:


242

Il modo più semplice è usare l'argomento di profondità

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

Tuttavia, ciò includerà solo le relazioni per le relazioni future, che in questo caso non è proprio ciò di cui hai bisogno, poiché il campo degli insegnanti è una relazione inversa.

Se hai requisiti più complessi (ad es. Includere relazioni inverse, nidificare alcuni campi, ma non altri o nidificare solo un sottoinsieme specifico di campi) puoi nidificare i serializzatori , ad esempio ...

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

Si noti che utilizziamo l'argomento source sul campo serializzatore per specificare l'attributo da utilizzare come origine del campo. Potremmo eliminare l' sourceargomento assicurandoci invece che l' teachersattributo esista utilizzando l' opzione related_name sul Teachermodello, ad es.classroom = models.ForeignKey(Classroom, related_name='teachers')

Una cosa da tenere a mente è che i serializzatori nidificati attualmente non supportano le operazioni di scrittura. Per le rappresentazioni scrivibili, è necessario utilizzare rappresentazioni piane regolari, come pk o hyperlinking.


Quando ho provato la prima soluzione non ho ricevuto gli Insegnanti, tuttavia ho ricevuto istanze del genitore dell'Aula (che questo esempio non mostra). Nella seconda soluzione ho ricevuto un errore: "L'oggetto 'Classroom' non ha attributo 'insegnanti'". Mi sto perdendo qualcosa?
Chaz,

1
@Chaz Aggiornata la risposta per spiegare perché depthnon dovrebbe fare ciò di cui hai bisogno in questo caso, e per spiegare l'eccezione che stai vedendo e come gestirla .
Tom Christie,

1
Sono un idiota e colpivo il server sbagliato. Funziona sicuramente attraverso molte o molte relazioni.
yellottyellott,

15
I serializzatori di nidificazione sono fantastici! Ho dovuto fare questo e stavo usando DRF 3.1.0. Ho dovuto includere many=Truecosì ...TeacherSerializer(source='teacher_set', many=True). Altrimenti stavo ottenendo il seguente errore: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
Il retro di un ForeignKey verrà nominato ..._setper impostazione predefinita. Vedi i documenti Django per maggiori dettagli: docs.djangoproject.com/en/1.10/ref/models/relations/…
Tom Christie,

36

Grazie @ TomChristie !!! Mi hai aiutato molto! Vorrei aggiornarlo un po '(a causa di un errore che ho riscontrato)

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",)

2

Questo può essere realizzato anche usando un dandy dandy molto pratico confezionato chiamato drf-flex-fields . Lo usiamo ed è davvero fantastico. Basta installarlo pip install drf-flex-fields, passarlo attraverso il serializzatore, aggiungere expandable_fieldse bingo (esempio di seguito). Inoltre, consente di specificare relazioni nidificate profonde utilizzando la notazione punto.

from rest_flex_fields import FlexFieldsModelSerializer

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

Quindi aggiungi ?expand=teacher_setl'URL e restituisce una risposta estesa. Spero che questo aiuti qualcuno, un giorno. Saluti!

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.