Django admin annotation - Сумма полей связанных моделей - PullRequest
0 голосов
/ 26 марта 2020

У меня есть панель инструментов / листинг django -admin, которая пытается отслеживать и суммировать «задания» с аннотированным набором запросов, который, я думаю, не работает должным образом. Как часть таблицы, я отобразил вычисленные поля для томов данных из связанных объектов (как предусмотрено аннотированным набором запросов).

По сути, это список заданий, которые отслеживают работу с компьютерными устройствами и их соответствующими устройствами хранения, так что Job может иметь несколько устройств , которые могут иметь несколько Storage устройств. Как устройства, так и хранилище могут содержать объем данных (так же, как телефон может иметь внутреннюю память и съемную карту памяти.) В некоторых заданиях устройство может быть обнаженным жестким диском или башней P C с 5 жесткими дисками, поэтому я пытаясь приспособить эти сценарии ios ...

models.py:

class Job(models.Model):
    ...
    job_number = models.CharField(max_length=50)
    job_name = models.CharField(max_length=100)
    ...

class Device(models.Model):
    ...
    device_number = models.CharField(max_length=50)
    job = models.ForeignKey(Job, null=True, on_delete=models.CASCADE)
    capacity = models.FloatField(null=True)
    ...

class Storage(models.Model):
    ...
    storage_number = models.CharField(max_length=50)
    device = models.ForeignKey(Device, null=True, on_delete=models.CASCADE)
    capacity = models.FloatField(null=True)
    ...

Для модели администратора для Job я попробовал следующий подход, который кажется для работы, однако, когда я фильтрую (например, на основе текстового поиска по названию работы) итоги объема данных, увеличиваются

admin.py

class JobAdmin(admin.ModelAdmin)

    def get_queryset(self, request):
        queryset = super().get_queryset(request)
        queryset = queryset.annotate(
            _device_count=Count("device", distinct=True),
            _device_volume=Sum("device__capacity", distinct=True)+Sum("device__storage__capacity", distinct=True)
           )
        return queryset

    def data_label(self,obj):
        return obj._device_volume

I Заметил, что если я выполняю поиск / фильтрацию по названию работы и получаю несколько совпадений, то получается, что сумма данных умножается на количество результатов, но если я фильтрую по какому-либо другому полю (используя поля list_filter), этого не происходит.

Может кто-нибудь увидеть, где я иду не так? Я ценю любые советы.

Хан

1 Ответ

1 голос
/ 26 марта 2020

Старайтесь не объединять несколько агрегатов с annotate () , поскольку приведет к неверным результатам (поскольку объединения используются вместо подзапросов )

Так как это сделать? Используйте подзапросы , что-то вроде строки должно сделать работу:

from django.db.models import OuterRef, Subquery, Sum, Count


def get_queryset(self, request):
    queryset = super().get_queryset(request)
    queryset = queryset.annotate(
        _device_count=Subquery(
            Device.objects.filter(job=OuterRef("pk"))
            .values("job")
            .annotate(cnt=Count("id"))
            .values("cnt")
        ),
        _device_capacity=Subquery(
            Device.objects.filter(job=OuterRef("pk"))
            .values("job")
            .annotate(vol=Sum("capacity"))
            .values("vol")
        ),
        _storage_capacity=Subquery(
            Storage.objects.filter(device__job=OuterRef("pk"))
            .values("device__job")
            .annotate(vol=Sum("capacity"))
            .values("vol")
        ),
    )
    return queryset

Вы можете попробовать сделать еще одну аннотацию, чтобы получить сумму _device_capacity и _storage_capacity, но я думаю, было бы достаточно просто сложить это в python, так что, возможно, нет необходимости беспокоить базу данных.

...