Django在一次调用中序列化多个对象

use*_*926 7 python django django-rest-framework

我想知道如何在序列化时减少对数据库的调用次数:

我有以下两种型号:

 class House(models.Model):
     name = models.CharField(max_length = 100, null = True, blank = True)
     address = models.CharField(max_length = 500, null = True, blank = True)

 class Room(models.Model):
      house = models.ForeignKey(House)
      name  = models.CharField(max_length = 100)
Run Code Online (Sandbox Code Playgroud)

有1个房子,它可以有多个房间.

我正在使用django-rest-framework并尝试在房屋级别将所有3个事物序列化.

 class HouseSerializer(serializers.ModelSerializer)
    rooms = serializers.SerializerMethodField('room_serializer')

    def room_serializer(self):
         rooms = Room.objects.filter(house_id = self.id) # we are in House serializer, so self is a house
         return RoomSerializer(rooms).data

    class Meta:
        model = House
        fields = ('id', 'name', 'address')
Run Code Online (Sandbox Code Playgroud)

所以现在,对于我想要序列化的每个房子,我需要单独调用它的房间.它有效,但这是一个额外的电话.(想象一下我试图把很多东西包在一起!)

现在,如果我有100个房子,为了序列化所有内容,我需要在O(n)时间内进行100次数据库命中

如果我可以将所有信息放在一起,我知道我可以减少到2次点击.O(1)时间

my_houses = Houses.objects.filter(name = "mine")
my_rooms = Rooms.objects.filter(house_id__in = [house.id for house in my_houses])
Run Code Online (Sandbox Code Playgroud)

我的问题是我该怎么做?并让序列化器快乐?

在做完两个电话后,我可以以某种方式做一个循环,将一个房间"附加"到一个房子,然后序列化吗?(我可以添加这样的属性吗?)如果可以,我如何让我的序列化程序读取它?

请注意,我不需要django-rest-serializer来允许我以这种方式更改Rooms中的属性.这仅适用于GET.

Kev*_*own 12

正如目前所写,使用a SerializerMethodField,您正在进行N + 1个查询.我已经在Stack Overflow上讨论了几次以优化数据库查询,一般来说,它类似于如何提高Django的性能.您正在处理一对多关系,该关系可以与多对多关系一样进行优化prefetch_related.

class HouseSerializer(serializers.ModelSerializer)
    rooms = RoomSerializer(read_only=True, source="room_set", many=True)

    class Meta:
        model = House
        fields = ('id', 'name', 'address', )
Run Code Online (Sandbox Code Playgroud)

我所做的更改使用嵌套序列化程序而不是手动生成序列化程序SerializerMethodField.我已经限制它read_only,正如你所提到的,你只需要它来处理GET请求,而可写的序列化程序在Django REST Framework 2.4中有问题.

由于尚未设置Room- > House关系的反向关系,因此它是默认值room_set.通过设置你可以(也应该)重写此related_nameForeignKey领域,你需要调整source相应.

为了防止N + 1查询问题,您需要覆盖视图上的查询集.在通用视图的情况下,这将在queryset属性上或在get_queryset方法内完成queyset = House.objects.prefetch_related('room_set').这将请求所有相关的房间以及House对象的请求,因此,除了N + 1个请求,您将只有两个请求.