2014-11-30 4 views
2

мне было интересно, как я могу уменьшить количество звонков в моей базе данных при сериализации:Django сериализации нескольких объектов в одном вызове

У меня есть следующие 2 модели:

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) 

Существует один дом, он может иметь несколько комнат.

Я использую 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') 

Итак, для каждого дома, который я хочу сериализовать, мне нужно сделать отдельный звонок для его номеров. Это работает, но это дополнительный звонок. (представьте меня пытаются упаковать много вещей вместе!)

Теперь, если у меня было 100 домов, сериализовать все, я должен был бы сделать 100 баз данных хитов, O (п)

Я знаю, Я могу уменьшить это до 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]) 

Мой вопрос: как я могу это сделать? и заставить сериализаторы быть счастливыми?

Могу ли я как-то сделать цикл после выполнения моих двух вызовов, «прикрепить» комнату к дому, а затем сериализовать его? (могу ли я добавить такой атрибут?) Если смогу, как мне заставить мой сериализатор читать его?

Обратите внимание, что мне не нужен django-rest-serializer, чтобы я мог изменять атрибуты в номерах, таким образом. Это только для GET.

ответ

5

Как это написано в настоящее время, используя SerializerMethodField, вы делаете N + 1 запросов. Я несколько раз накрывал это на Stack Overflow для optimizing the database queries, и в общем, он похож на то, как вы бы improve the performance in 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',) 

Изменение я сделал использует вложенные сериализаторы вместо того, чтобы вручную генерации сериалайзер в пределах SerializerMethodField. Я ограничил это read_only, поскольку вы упомянули, что вам нужно только для GET. Запросы и перезаписываемые сериализаторы имеют проблемы в Django REST Framework 2.4.

Как ваше обратное отношение для отношения Room ->House не было установлено, это значение по умолчанию room_set. Вы можете (и должны) отменить это, установив related_name в поле ForeignKey, и вам нужно будет отрегулировать source соответственно.

Во избежание проблемы с запросом N + 1 вам необходимо переопределить набор запросов в вашем представлении. В случае общего представления это будет сделано в атрибуте queryset или в методе get_queryset, например queyset = House.objects.prefetch_related('room_set'). Это запросит все связанные номера вдоль запроса на объект House, поэтому вместо запросов N + 1 у вас будет только два запроса.

+0

Хотелось бы, чтобы у меня было более одного перестрелки. Спасибо, что спасли мне около 500 запросов! – Kobold

+0

Это было очень полезно. Спасибо! – gregdevs

Смежные вопросы