Django получить значения для Макс сгруппированных данных - PullRequest
1 голос
/ 21 апреля 2020

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

Вот простая модель. Допустим, у нас есть модель Book и модель Reserve, которая содержит данные резервирования для каждой книги.

class Book(models.Model):
    title = models.CharField(
        'Book Title',
        max_length=50
    )

    name = models.CharField(
        max_length=250
    )


class Reserve(models.Model):
    book = models.ForeignKey(
        Book,
        on_delete=models.CASCADE
    )
    reserve_date = models.DateTimeField()

    status = models.CharField(
        'Reservation Status',
        max_length=5,
        choices=[
            ('R', 'Reserved'),
            ('F', 'Free')
        ]
    )

Я добавил книгу и две записи резервирования для модели:

from django.utils import timezone

book_inst = Book(title='Book1')
book_inst.save()

reserve_inst = Reserve(book=book_inst, reserve_date=timezone.now(), status='R')
reserve_inst.save()

reserve_inst = Reserve(book=book_inst, reserve_date=timezone.now(), status='F')
reserve_inst.save()

My Цель состоит в том, чтобы получить данные о последнем бронировании для каждой книги. Основываясь на других вопросах, я понял это следующим образом:

from django.db.models import F, Q, Max


reserve_qs = Reserve.objects.values(
    'book__title'
)

reserve_qs теперь выполняет последнее действие для каждой книги, но когда я добавляю .value(), он игнорирует группировку и возвращает все записи.

Я также пытался фильтровать с помощью F:

Reserve.objects.values(
    'book__title'
).annotate(
    last_action=Max('reserve_date')
).values(

).filter(
    reserve_date=F('last_action')
)

Я использую Django 3 и SQLite.

Ответы [ 2 ]

1 голос
/ 21 апреля 2020

Используя другой фильтр, вы нарушите механизм GROUP BY. Однако вы можете просто получить последнее резервирование с помощью:

from django.db.models import F, Max

Reserve.objects.filter(
    book__title='Book1'
).annotate(
    book_title=F('book__title'),
    last_action=<b>Max('book__reserve__reserve_date')</b>
).filter(
    reserve_date=F('last_action')
)

или для всех книг:

from django.db.models import F, Max

qs = Reserve.objects.annotate(
    book_title=F('book__title'),
    last_action=<b>Max('book__reserve__reserve_date')</b>
).filter(
    reserve_date=F('last_action')
).select_related('book')

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

Это извлечет все последние резервирования для всех Book s, которые сохраняются после фильтрации. Обычно это один на Book. Но если есть несколько Book s с несколькими Reservation s с одинаковой отметкой времени, то будет возвращено несколько единиц.

Так что мы можем, например, напечатать резервирование с помощью:

for q in qs:
    print(
        'Last reservation for {} is {} with status {}',
        q.book.title,
        q.reserve_date,
        q.status
    )

Для отдельной книги, однако, лучше просто извлечь объект Book и вернуть .latest(..) [Django -doc] Повторное сохранение:

Book.objects.get(title='Book1')<b>.reserve_set.latest('reserve_date')</b>
0 голосов
/ 21 апреля 2020
book_obj = Book.objects.get(title='Book1')
reserve_qs = book_obj.reserve_set.all()

Возвращает все резервы, которые содержат эту книгу.
Вы можете получить последний объект, используя .first или .last(), или отсортировать их.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...