Django: 嵌套属性的Prefetch_related

问题描述 投票:0回答:1

我需要列出我所有的设备。为了做到这一点,我使用了一个预取相关,以减少查询量。但其中一个是消耗了太多的时间... ... 我想知道,如果它不能去更好的。

我将从模型构造开始。我想要一个设备列表

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

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):
    """
    GET the data from all the devices.
    """

    devices = (
        Device.objects.filter()
        .all()
        .prefetch_related(
            "site__users",
            "software_update_history",
            "supplier",
            Prefetch(
                "gatewaydevices",
                queryset=GatewayDevice.objects.filter(end_date=None)
                .order_by()
                .distinct()
                .prefetch_related("gateway"),
            ),
        )
    )

    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):
    return (
        GatewaySimpleSerializer(device.gatewaydevices.gateway).data
        if device.gatewaydevices.gateway
        else None
    )

我尝试了不同的方法 这个是当前的方法... 现在我得到这个错误。

AttributeError: 'RelatedManager' object has no attribute 'gateway'
django prefetch
1个回答
0
投票

首先,你应该能够使用 .select_related("gateway") 而非 .prefetch_related("gateway"). 这应该可以为你节省一个查询的时间。

你的问题似乎在这里。

if device.gatewaydevices.gateway

这里 gatewaydevices 不是一个单一的模型实例,所以它没有网关属性。我不太清楚你的用例是什么,但可以有多个 gatewaydevices. 也许你想要的是第一个(如果有零,这将会失败。)。gatewaydevices):

if device.gatewaydevices.first().gateway

也许你想知道是否有什么。这应该是可行的。

if device.gatewaydevices.filter(gateway__isnull=False)

这看起来很奇怪, 感觉这段代码应该和查询的其他部分在一起 如果你想排除对象,而不需要. . gateways. 我觉得你应该把 if 并总是返回数据。你这样做并没有节省任何数据库时间。

基于进一步的评论。

if device.gatewaydevices.filter(end_date__isnull=True).first()

如果你想避免额外的工作,一个(可能)更好的方法来写查询。

gateway_devices = (
    GatewayDevice.objects
    .filter(end_date_isnull=True)
    .select_related("gateway", "device")
)

这将在一个查询中得到所有的东西,你可以只使用简单的属性访问来得到相关的对象,而不需要任何额外的查询,例如:

for gateway_device in gateway_devices:
   print(gateway_device.device.name)

看看你的模型(虽然我不能看到我需要写的所有东西) 看起来你还想从你的模型中得到一些信息。Device 的关系,所以你可能需要调整这一点,使其具有 device 预取你所需要的东西,就像。

gateway_devices = (
    GatewayDevice.objects
    .filter(end_date_isnull=True)
    .select_related("gateway")
    .prefetch_related(
        Prefetch(
            "device",
            queryset=Device.objects.prefetch_related(
                # Depending how these are related you may be able to
                # save some queries using `select_related()` instead.
                "site__users", "software_update_history", "supplier"
            )
        )
    )
)

这可能不是完美的, 但它应该给你什么 你需要做一个好的开始。

© www.soinside.com 2019 - 2024. All rights reserved.