Как включить связанные поля модели с помощью Django Rest Framework?

154

Допустим, у нас есть следующая модель:

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

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

Предположим, что вместо получения результата, подобного этому, для функции ManyRelatedPrimaryKeyField:

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

вернуть что-то, что включает в себя полное представление связанной модели, например:

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

Это возможно? Если так, то как? И это плохая идея?

Чез
источник

Ответы:

242

Самый простой способ - использовать аргумент глубины.

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

Однако это будет включать только отношения для прямых отношений, что в данном случае не совсем то, что вам нужно, поскольку поле учителя - это обратные отношения.

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

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

Обратите внимание, что мы используем аргумент источника в поле сериализатора, чтобы указать атрибут, который будет использоваться в качестве источника поля. Мы могли бы отбросить sourceаргумент, вместо этого убедившись, что teachersатрибут существует, используя опцию related_name в вашей Teacherмодели, т.е.classroom = models.ForeignKey(Classroom, related_name='teachers')

Следует иметь в виду, что вложенные сериализаторы в настоящее время не поддерживают операции записи. Для доступных для записи представлений вы должны использовать обычные плоские представления, такие как pk или гиперссылка.

Том Кристи
источник
Когда я попробовал первое решение, я не получил Учителей, однако я действительно получил экземпляры родителя Классной комнаты (который этот пример не показывает). Во втором решении я получил ошибку - «Объект класса» не имеет атрибута «учителя». Я что-то упускаю?
Чаз
1
@Chaz Обновил ответ, чтобы объяснить, почему depthне следует делать то, что вам нужно в этом случае, а также объяснить исключение, которое вы видите, и как с ним бороться.
Том Кристи
1
Я идиот и попал не на тот сервер. Это определенно работает во многих отношениях.
yellottyellott
15
Вложенные сериализаторы - это круто! Я должен был сделать это и использовал DRF 3.1.0. Я должен был включать в себя many=Trueкак так ...TeacherSerializer(source='teacher_set', many=True). В противном случае я получил следующую ошибку: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
Обратная сторона ForeignKey будет названа ..._setпо умолчанию. См. Документацию Django для получения более подробной информации: docs.djangoproject.com/en/1.10/ref/models/relations/…
Том Кристи,
36

Спасибо @TomChristie !!! Вы мне очень помогли! Я хотел бы обновить это немного (из-за ошибки, с которой я столкнулся)

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

Это также может быть достигнуто с помощью довольно удобного dandy django, упакованного под названием drf-flex-fields . Мы используем это, и это довольно здорово. Вы просто устанавливаете его pip install drf-flex-fields, пропускаете через сериализатор, добавляете expandable_fieldsи бинго (пример ниже). Это также позволяет вам указывать глубокие вложенные отношения с помощью точечной нотации.

from rest_flex_fields import FlexFieldsModelSerializer

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

Затем вы добавляете ?expand=teacher_setв свой URL, и он возвращает расширенный ответ. Надеюсь, это когда-нибудь поможет. Ура!

Пол Такетт
источник