Como incluir campos de modelo relacionados usando o Django Rest Framework?


153

Digamos que temos o seguinte modelo:

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

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

Digamos que, em vez de obter um resultado como este pela função ManyRelatedPrimaryKeyField:

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

retorne algo que inclua a representação completa do modelo relacionado, como:

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

Isso é possível? Se sim, como? E isso é uma má ideia?

Respostas:


242

A maneira mais simples é usar o argumento de profundidade

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

No entanto, isso incluirá apenas relacionamentos para relacionamentos futuros, o que, nesse caso, não é exatamente o que você precisa, pois o campo de professores é um relacionamento inverso.

Se você possui requisitos mais complexos (por exemplo, inclua relacionamentos reversos, aninhe alguns campos, mas não outros, ou aninhe apenas um subconjunto específico de campos), poderá aninhar serializadores , por exemplo ...

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

Observe que usamos o argumento de origem no campo serializador para especificar o atributo a ser usado como a origem do campo. Poderíamos descartar o sourceargumento, garantindo que o teachersatributo exista usando a opção related_name no seu Teachermodelo, ou seja.classroom = models.ForeignKey(Classroom, related_name='teachers')

Uma coisa a ter em mente é que os serializadores aninhados atualmente não suportam operações de gravação. Para representações graváveis, você deve usar representações planas regulares, como pk ou hiperlink.


Quando tentei a primeira solução, não recebi os Professores, no entanto, recebi instâncias dos pais da Sala de Aula (o que este exemplo não mostra). Na segunda solução, recebi um erro - "O objeto 'Classroom' não tem atributo 'professores'". Estou esquecendo de algo?
Chaz

1
@Chaz Atualizou a resposta para explicar por depthque não faria o que você precisa neste caso e para explicar a exceção que está vendo e como lidar com isso.
Tom Christie

1
Eu sou um idiota e estava atingindo o servidor errado. Definitivamente funciona em muitos para muitos relacionamentos.
Yellottyellott # 30/13

15
Serializadores de aninhamento são incríveis! Eu tive que fazer isso e estava usando o DRF 3.1.0. Eu tive que incluir many=Trueassim ...TeacherSerializer(source='teacher_set', many=True). Caso contrário, eu estava recebendo o seguinte erro: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 08/04

2
O verso de uma ForeignKey será nomeado ..._setpor padrão. Veja a documentação do Django para mais detalhes: docs.djangoproject.com/en/1.10/ref/models/relations/...
Tom Christie

36

Obrigado @TomChristie !!! Você me ajudou muito! Gostaria de atualizar um pouco (por causa de um erro que encontrei)

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

Isso também pode ser feito usando um django dandy bastante útil chamado drf-flex-fields . Nós o usamos e é incrível. Você acabou de instalar pip install drf-flex-fields, passar pelo serializador, adicionar expandable_fieldse bingo (exemplo abaixo). Ele também permite que você especifique relacionamentos aninhados profundos usando a notação de ponto.

from rest_flex_fields import FlexFieldsModelSerializer

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

Então você adiciona ?expand=teacher_setao seu URL e ele retorna uma resposta expandida. Espero que isso ajude alguém, algum dia. Felicidades!

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.