Django.rest_framework: How to serialize one to man

2019-03-11 08:10发布

问题:

I have some troubles serializing with django. I have three models, let's say a School, a Room and a Desk (dummy name for example). Each School have multiple Room, and each Room have multiple Desk.

The classes and their relations look like this :

class School(models.Model):
    name = models.CharField()

class Room(models.Model):
    name = models.CharField()
    school_id = models.ForeignKey(School)

class Desk(models.Model):
    row = models.IntegerField()
    col = models.IntegerField()
    room_id = models.ForeignKey(Room)

I want to be able to serialize a list of School, each directly containing all the desks inside.

The closet I got was by writing in my serialize.py three serializer :

class DeskSerializer(serializers.ModelSerializer):
    class Meta:
        field = (row, col,)

class RoomSerializer(serializers.ModelSerializer):

    desks = DeskSerializer(source='desk_set', many=True)
    class Meta:
        field = (desks,)

class SchoolSerializer(serializers.ModelSerializer):

    rooms = RoomSerializer(source='room_set', many=True)
    class Meta:
        field = (name, rooms,)

Which return a list of school containing a list of room containing a list of desk, when I want a list of School containing a list of desk

Which source should I use in the School serializer to return directly the desk? Something like source='room_set.desk_set'? Or maybe by using a transform_ function?

PS: the code is write from scratch on the post, please ignore the possible syntax errors

回答1:

If I'm understanding you correctly, you want the SchoolSerializer to return a nested structure 2 levels deep, but skipping the intermediate model. To do this, I would create a method in your School model to retrieve the Desk instances:

class School(models.Model):
    ...

    def get_desks(self):
        rooms = Room.objects.filter(school=self)
        desks = Desk.objects.filter(room__in=rooms)
        return desks

Then, in your SchoolSerializer include a field that uses that method and renders the returned instances as you wish via your DeskSerializer:

class SchoolSerializer(serializers.ModelSerializer):
    ...
    desks = DeskSerializer(
        source='get_desks',
        read_only=True
    )

    class Meta:
        field = (name, desks)

The key to understanding how this works is that the model method used as the value for the source argument must simply return instances of that serializer's model. The serializer takes it from there, rendering the object(s) however you defined them within that serializer (in this case the DeskSerializer).

Hope this helps.