Как получить одну из записей обратного внешнего ключа, когда в наборе запросов модели - PullRequest
0 голосов
/ 07 февраля 2020

Я работаю на веб-сайте django для отслеживания устройств в сети.

У меня есть модель Device:

class DeviceManager(models.Manager):
    def get_queryset(self):
        return super().get_queryset().annotate(
            num_interfaces=Count('interface', distinct=True))

class Device(models.Model):
    objects = DeviceManager()
    uuid = models.UUIDField()
    name = models.CharField(max_length=30)

    class Meta:
        _base_manager = DeviceManager()

, а затем у меня есть модель Interface:

from .models import Device

class Interface(models.Model):
    ip = models.GenericIPAddressField()
    device = models.ForeignKey(Device, on_delete=models.CASCADE)

При отображении списка устройств мне нужно указать количество интерфейсов для каждого устройства. Поэтому я создал диспетчер, который извлекает счет одним запросом, вместо того, чтобы иметь отдельный запрос для каждого устройства в списке.

Теперь я хочу отобразить случайный IP-адрес для каждого устройства. Это не так, если устройство имеет несколько интерфейсов.

Я начал писать еще один .annotate в свой DeviceManger, и я не уверен, что делать.

Я думал об использовании подзапроса, что-то вроде:

subquery = Interface.objects.filter(ip=OuterRef('ip'))

так:

class DeviceManager(models.Manager):
    def get_queryset(self):
        subquery = Interface.objects.filter(device_id=OuterRef('device'))

        return super().get_queryset().annotate(
            num_interfaces=Count('interface', distinct=True)).annotate(
            ip=Coalesce(Min(Subquery(subquery('ip'))), None)

Но потом я понял, что Interface зависит от Device, и поэтому я не могу обратиться к Interface до тех пор, пока Device не будет полностью определено, поэтому определенно не в DeviceManager

Каков наилучший способ сделать это?

1 Ответ

0 голосов
/ 07 февраля 2020

Ты довольно близко. Хотя я не рекомендую добавлять эти методы к методу get_queryset вашего менеджера, если только он вам буквально не нужен для подавляющего большинства запросов. Вместо этого создайте отдельную функцию или сгенерируйте запросы при необходимости.

from django.db.models import OuterRef, Subquer
interfaces = Interface.objects.filter(device_id=OuterRef('id')).order_by('?')
Device.objects.annotate(
    num_interfaces=Count('interface', distinct=True),
    random_ip=Subquery(interfaces.values('ip')[:1]),
)
...