Bagaimana cara saya menyertakan bidang model terkait menggunakan Django Rest Framework?

153

Katakanlah kita memiliki model berikut:

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

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

Katakanlah alih-alih mendapatkan hasil seperti ini per fungsi ManyRelatedPrimaryKeyField:

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

minta itu mengembalikan sesuatu yang mencakup representasi model terkait penuh seperti:

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

Apakah ini mungkin? Jika ya, bagaimana caranya? Dan apakah ini ide yang buruk?

Chaz
sumber

Jawaban:

242

Cara paling sederhana adalah dengan menggunakan argumen kedalaman

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

Namun, itu hanya akan mencakup hubungan untuk hubungan ke depan, yang dalam hal ini tidak cukup apa yang Anda butuhkan, karena bidang guru adalah hubungan terbalik.

Jika Anda memiliki persyaratan yang lebih kompleks (mis. Menyertakan hubungan terbalik, membuat sarang beberapa bidang, tetapi tidak yang lain, atau hanya membuat subkumpulan bidang tertentu), Anda dapat membuat serializers , misalnya ...

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

Perhatikan bahwa kami menggunakan argumen sumber pada bidang serializer untuk menentukan atribut yang akan digunakan sebagai sumber bidang. Kami dapat menjatuhkan sourceargumen dengan memastikan teachersatribut tersebut ada dengan menggunakan opsi related_name pada Teachermodel Anda , yaitu.classroom = models.ForeignKey(Classroom, related_name='teachers')

Satu hal yang perlu diingat adalah bahwa serializers bersarang saat ini tidak mendukung operasi penulisan. Untuk representasi yang dapat ditulis, Anda harus menggunakan representasi flat biasa, seperti pk atau hyperlink.

Tom Christie
sumber
Ketika saya mencoba solusi pertama saya tidak menerima Guru, namun saya menerima contoh orang tua dari Kelas (yang tidak ditunjukkan contoh ini). Dalam solusi kedua saya menerima kesalahan - "objek 'Kelas' tidak memiliki atribut 'guru'". Apakah saya melewatkan sesuatu?
Chaz
1
@Chaz Memperbarui jawaban untuk menjelaskan mengapa depthtidak melakukan apa yang Anda butuhkan dalam kasus ini, dan untuk menjelaskan pengecualian yang Anda lihat dan cara mengatasinya.
Tom Christie
1
Saya seorang idiot dan sedang memukul server yang salah. Ini pasti bekerja di banyak hubungan banyak.
yellottyellott
15
Serialis bersarang luar biasa! Saya harus melakukan ini dan menggunakan DRF 3.1.0. Saya harus memasukkan many=Trueseperti itu ...TeacherSerializer(source='teacher_set', many=True). Kalau tidak, saya mendapatkan kesalahan berikut: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
Sisi kebalikan dari ForeignKey akan dinamai ..._setsecara default. Lihat dokumen Django untuk detail lebih lanjut: docs.djangoproject.com/en/1.10/ref/models/relations/…
Tom Christie
36

@TomChristie terima kasih !!! Anda banyak membantu saya! Saya ingin memperbarui itu sedikit (karena kesalahan yang saya alami)

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
sumber
2

Ini juga dapat dicapai dengan menggunakan django keren yang sangat menarik yang disebut dengan drf-flex-fields . Kami menggunakannya dan itu sangat mengagumkan. Anda cukup menginstalnya pip install drf-flex-fields, meneruskannya melalui serializer Anda, tambahkan expandable_fieldsdan bingo (contoh di bawah). Ini juga memungkinkan Anda untuk menentukan hubungan bersarang yang mendalam dengan menggunakan notasi titik.

from rest_flex_fields import FlexFieldsModelSerializer

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

Kemudian Anda menambahkan ?expand=teacher_setke URL Anda dan mengembalikan respons yang diperluas. Semoga ini bisa membantu seseorang, suatu hari nanti. Bersulang!

Paul Tuckett
sumber