我需要列出我所有的设备。为了做到这一点,我使用了一个预取相关,以减少查询量。但其中一个是消耗了太多的时间... ... 我想知道,如果它不能去更好的。
我将从模型构造开始。我想要一个设备列表
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'
首先,你应该能够使用 .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)
这看起来很奇怪, 感觉这段代码应该和查询的其他部分在一起 如果你想排除对象,而不需要. . gateway
s. 我觉得你应该把 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"
)
)
)
)
这可能不是完美的, 但它应该给你什么 你需要做一个好的开始。