Django prefetch_related требует много времени выполнения - PullRequest
0 голосов
/ 06 мая 2020

Мне нужно перечислить все мои устройства. Для этого я использую предварительную выборку, связанную с уменьшением количества запросов. Но один из них отнимает много времени .. Интересно, не может ли он go лучше.

Начну с построения модели: мне нужен список устройств. Это модель устройства:

class Device(models.Model):
    name = models.CharField(max_length=250, null=True, blank=True)

    def get_active_gateway(self):
        from backend.gateways.models import Gateway

        all_gatewaydevices = self.gatewaydevices.all()
        for gd in all_gatewaydevices:
            if not gd.end_date:
                return gd.gateway
        return None

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

Модель gatewaydevice выглядит так:

class GatewayDevice(models.Model):
    gateway = models.ForeignKey(
        Gateway, on_delete=models.CASCADE, related_name="devices"
    )
    device = models.ForeignKey(
        Device, on_delete=models.CASCADE, related_name="gatewaydevices"
    )

Итак, в моем списке устройств, Мне нужен связанный шлюз для каждого устройства.

Это мое мнение:

class AdminDeviceView(GenericAPIView):
    def get_permissions(self):
        return IsAuthenticated()

    # noinspection PyMethodMayBeStatic
    def get_serializer_class(self):
        return AdminDeviceInfoSerializer

    @swagger_auto_schema(
        responses={
            200: openapi.Response(
                _("Successfully fetched all data from devices."),
                AdminDeviceInfoSerializer,
            )
        }
    )
    def get(self, request):
        devices = (
            Device.objects.prefetch_related(
                "gatewaydevices__gateway",
            )
            .all()
        )

        serializer_class = self.get_serializer_class()
        serializer = serializer_class(devices, many=True)
        devices_data = serializer.data

        return Response(
            {"total": devices.count(), "items": devices_data}, status=status.HTTP_200_OK
        )

Это важная часть сериализатора:

@staticmethod
def get_gateway(device):
    gateway = device.get_active_gateway()
    return GatewaySimpleSerializer(gateway).data if gateway else None

Может ли это стать быстрее / эффективнее?

1 Ответ

0 голосов
/ 06 мая 2020

get_active_gateway очень неэффективно - проверяет каждый объект отдельно. А при вызове в get_gateway - он не использует бонусы предварительной выборки.


Вы можете фильтровать и получать только действительные Gateway объекты непосредственно в предварительной выборке.

Также используя to_attr для сопоставления результата с тем же именем поля, что и в сериализаторе.

(* не проверено)

class AdminDeviceView(GenericAPIView):
    # ...
    def get(self, request):
    # ...
        devices = Device.objects.prefetch_related(
            Prefetch(
                "gatewaydevices",
                queryset=Gateway.objects.filter(
                    devices__end_date__isnull=True
                ).order_by().distinct(), # distinct in case there are many
                to_attr="gateway",
            )
        )
    # ...


class AdminDeviceInfoSerializer(serializers.ModelSerializer):
    gateway = GatewaySimpleSerializer(many=True, read_only=True)
    # ...
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...