Запрос django подзапроса, чтобы получить самые большие значения disctint от объектов и возвратить те объекты - PullRequest
0 голосов
/ 08 октября 2019

Итак, у меня есть этот запрос, который мне нужно повторно отфильтровать, чтобы получить только наибольшее значение поля take_at из отдельного идентификатора счетчика, но я не могу получить часть django orm's / sql, которая выполняет этот запрос.

<QuerySet [<Reading: [127] meter installation: 29, type: 1, taken: 2019-10-07 16:06:48.101453+00:00 value: 78.0000, comment: , VAT: 22.00>, <Reading: [126] meter installation: 41, type: 2, taken: 2019-10-07 14:05:32.415905+00:00 value: 7.0000, comment: asdfe, VAT: None>, <Reading: [125] meter installation: 41, type: 2, taken: 2019-10-07 14:02:37.588983+00:00 value: 7.0000, comment: asdfe, VAT: None>, <Reading: [124] meter installation: 49, type: 2, taken: 2019-10-07 12:19:49.067398+00:00 value: 8.0000, comment: , VAT: 2.00>

этот запрос содержит множество объектов чтения, но мне нужно получить только самое большое значение take_at из отдельных установок счетчиков, я попытался создать аннотацию, а затем развести, но они не реализованы вместе, яЯ новичок в SQL, поэтому любая помощь будет отличной!

reading.py

class Reading(DateTrackedModel):
    meter_installation = models.ForeignKey(
        "MeterInstallation",
        on_delete=models.PROTECT,
        related_name="readings",
        null=False,
        blank=False,
        verbose_name=_("Meter Installation"),
    )
    value = models.DecimalField(
        decimal_places=4, max_digits=10, null=False, blank=False, default=0, verbose_name=_("Value")
    )
    price = models.DecimalField(
        decimal_places=4, max_digits=10, null=False, blank=False, default=0, verbose_name=_("Price")
    )
    reading_type = models.ForeignKey(
        "MeterType",
        on_delete=models.PROTECT,
        null=False,
        blank=False,
        related_name="readings",
        verbose_name=_("Reading type"),
    )
    comment = models.TextField(null=False, blank=True, verbose_name=_("Comment"))
    taken_at = models.DateTimeField(null=False, default=now, blank=False, verbose_name=_("Taken at"))
    VAT = models.DecimalField(decimal_places=2, max_digits=10, null=True, blank=True, verbose_name=_("VAT"))
    unit_name = models.CharField(max_length=100, null=False, blank=True, unique=False, verbose_name=_("Unit name"))
    unit_price = models.DecimalField(
        decimal_places=4, max_digits=10, null=False, blank=False, default=0.0, verbose_name=_("Unit price")
    )

Модель установки метра:

class MeterInstallation(ActiveAfterUntilModel, DateTrackedModel, MPTTModel, NamedModel):  # type: ignore
    meter_type = models.ForeignKey(
        MeterType,
        on_delete=models.PROTECT,
        null=False,
        blank=False,
        related_name="installations",
        verbose_name=_("Meter Installation type"),
    )
    parent = TreeForeignKey(
        "self", on_delete=models.CASCADE, null=True, blank=True, related_name="children", db_index=True
    )
    meter = models.ForeignKey(
        Meter, on_delete=models.PROTECT, related_name="installations", null=False, blank=False, verbose_name=_("Meter")
    )
    building = models.ForeignKey(
        Building,
        on_delete=models.PROTECT,
        related_name="meter_installations",
        null=True,
        blank=False,
        verbose_name=_("Building"),
    )
    places = models.ManyToManyField(Place, related_name="meter_installations", blank=False, verbose_name=_("Places"))
    initial_reading = models.DecimalField(
        decimal_places=4, max_digits=10, null=False, blank=False, default=0, verbose_name=_("Initial reading")
    )
    final_reading = models.DecimalField(
        decimal_places=4, max_digits=10, null=True, blank=True, verbose_name=_("Final reading")
    )

1 Ответ

1 голос
/ 08 октября 2019

Не очень ясно, каков текущий статус вашего Reading QuerySet, но общий способ сделать то, что вы хотите, можно найти здесь в документации. В вашем случае это должно быть что-то вроде этого:

reading_qs.values('meter_installation').annotate(max_taken_at=models.Max('taken_at'))

ОБНОВЛЕНИЕ:

Так что это не очень понятно из первого поста, но у вас есть проблема. (в вашем случае n = 1)

Одним из способов решения этой конкретной версии проблемы является запрос окна (если ваша база данных поддерживает это). Должно быть что-то вроде этого:

reading_qs.annotate(max_taken_at=Window(
    expression=Max('taken_at'),
    partition_by=F('meter_installation')
)).filter(max_taken_at=F('taken_at'))

Другой подход будет через подзапрос , он будет выглядеть так:

reading_qs.annotate(
    max_taken_at=Subquery(
        reading_qs.filter(meter_installation=OuterRef('meter_installation'))
            .values('meter_installation')
            .annotate(max_taken_at=Max('taken_at'))
            .values('max_taken_at')
    )
).filter(max_taken_at=F('taken_at'))

Третье решение, которое будет PostgreSQLЕдинственное решение будет:

reading_qs.order_by(
    'meter_installation', '-taken_at'
).distinct('meter_installation')
...