Django, сгруппировать по одному полю, взять только последние / максимальные значения каждой группы и вернуть объекты ORM - PullRequest
0 голосов
/ 24 января 2020

У меня есть следующая Django (3.0) модель ORM:

class Portfolio(models.Model):
    code = models.CharField(max_length=20)
    name = models.CharField(max_length=100)
    date = models.DateField()
    created = models.DateTimeField(blank=True, null=True, auto_now_add=True)

Я хочу сгруппировать их по полю code и для каждого кода взять только портфель, который имеет максимум date

Я знаю, что могу использовать следующий запрос:

Portfolio.objects.values('code').annotate(latest=Max('date'))

Но у него есть три проблемы:

  1. Это дает мне только code и latest полей, поэтому я теряю другие поля
  2. Он возвращает словарь, в то время как я хочу получить список реальных Portfolio объектов
  3. Max работает, потому что date является DateField. Это также будет работать с другими типами полей цифр c, но что если я захочу упорядочить записи по значению CharField (лексикографический порядок c), например name, и взять первую запись для каждой группы?

Итак, подведем итог, мой вопрос: Как мне использовать Django ORM, чтобы получить список объектов ORM, сгруппированных по одному или нескольким полям, и получить обратно только первую запись каждой группе дано произвольное предложение "order by"?

1 Ответ

2 голосов
/ 24 января 2020

Получение первого / последнего значения из группы (групп) - это именно то, что DISTINCT ON с ORDER BY SQL предложение может использоваться для ( но afaik только в Postgresql. В MySQL просто DISTINCT, нет ON, поэтому - невозможно (напрямую), SQLite также не поддерживает DISTINCT ON, просто DISTINCT. Чтобы обозначить это, в Django позиционные аргументы .distinct() могут быть переданы только в Postgresql).

В Django мы можем сделать это с помощью QuerySet следующим образом:

Portfolio.objects.order_by().order_by(
    'code', # first, cause we want to group by this value
    '-created' # descending order, latest / max will be first
).distinct('code')

Здесь мы используем пустой .order_by() вызов __, очистить все существующие заказы __ on QuerySet (добавлено или по умолчанию), чтобы убедиться, что для правильной работы группировки применяется .order_by(...) со следующим *1034*.


Общий способ его использования:

  • Запуск QuerySet с примененными фильтрами - q = SomeModel.objects.filter(col1__gt=2)

  • очистить порядок, который уже установлен на QuerySet - q.order_by()

  • 'col1', 'col2', 'col3' - это столбцы / поля, которые мы хотим GROUP BY (для группировки)

  • 'col1', ' -col2 ',' -col3 ' - те же столбцы, по которым мы хотим сгруппировать, но с порядком, который мы хотим использовать для группировки (важно для всех подгрупп - все столбцы в списке групп , кроме первого - для них это повлияет на полученные строки из групп - 'first' или 'last' ; для первого столбца это не повлияет на строки результата, только на порядок следования)

  • '- date1' - любые дополнительные столбцы упорядочения по нашему выбору для упорядочения строк окончательной группы

Наконец добавьте предложение .distinct() с полями, которые мы выбираем для группировки в качестве аргументов, в том же порядке, что и в предложении .order_by() - q.distinct('col1', 'col2', 'col3')

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