Джанго: отличительные внешние ключи - PullRequest
10 голосов
/ 08 февраля 2011
class Log:
 project = ForeignKey(Project)
 msg = CharField(...)
 date = DateField(...)

Я хочу выбрать четыре самые последние записи журнала, где каждая запись журнала должна иметь уникальный внешний ключ проекта. Я пробовал решения для поиска в Google, но ни одно из них не работает, и документация по Django не очень хороша для поиска.

Я пробовал что-то вроде:

Log.objects.all().distinct('project')[:4]
Log.objects.values('project').distinct()[:4]
Log.objects.values_list('project').distinct('project')[:4]

Но это либо ничего не возвращает, либо записи в журнале того же проекта.

Любая помощь будет оценена!

Ответы [ 4 ]

17 голосов
/ 08 февраля 2011

Запросы не работают так - ни в ORM Django, ни в базовом SQL.Если вы хотите получить уникальные идентификаторы, вы можете запросить только идентификатор.Таким образом, вам нужно сделать два запроса, чтобы получить реальные записи журнала.Что-то вроде:

id_list = Log.objects.order_by('-date').values_list('project_id').distinct()[:4]
entries = Log.objects.filter(id__in=id_list)
3 голосов
/ 03 декабря 2011

На самом деле, вы можете получить project_ids в SQL.Предполагая, что вам нужны уникальные идентификаторы проектов для четырех проектов с последними записями журнала, SQL будет выглядеть следующим образом:

SELECT project_id, max(log.date) as max_date
FROM logs
GROUP BY project_id
ORDER BY max_date DESC LIMIT 4;

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

SELECT logs.*
FROM logs JOIN (
    SELECT project_id, max(log.date) as max_date
    FROM logs
    GROUP BY project_id
    ORDER BY max_date DESC LIMIT 4 ) as latest
ON logs.project_id = latest.project_id
   AND logs.date = latest.max_date;

Теперь, если вы иметь доступ к функциям управления окнами, это немного аккуратнее (я думаю, в любом случае), и, конечно, быстрее выполнять:

SELECT * FROM (
   SELECT logs.field1, logs.field2, logs.field3, logs.date
       rank() over ( partition by project_id 
                     order by "date" DESC ) as dateorder
   FROM logs ) as logsort
WHERE dateorder = 1
ORDER BY logs.date DESC LIMIT 1;

ОК, возможно, это не так легко понять, но поверьте мне на слово,он работает быстрее в большой базе данных.

Я не совсем уверен, каким образом это приводит к синтаксису объекта, или даже если это так.Кроме того, если вы хотите получить другие данные проекта, вам нужно присоединиться к таблице проектов.

1 голос
/ 04 мая 2018

Я знаю, что это старый пост, но в Django 2.0, я думаю, вы можете просто использовать:

0 голосов
/ 27 января 2019

Вам нужно два набора запросов. Хорошо, что это все равно приводит к единственному обращению к базе данных (хотя задействован подзапрос).

latest_ids_per_project = Log.objects.values_list(
    'project').annotate(latest=Max('date')).order_by(
    '-latest').values_list('project')

log_objects = Log.objects.filter(
     id__in=latest_ids_per_project[:4]).order_by('-date')

Это выглядит немного запутанно, но на самом деле приводит к удивительно компактному запросу:

SELECT "log"."id",
       "log"."project_id",
       "log"."msg"
       "log"."date"
FROM "log"
WHERE "log"."id" IN
    (SELECT U0."id"
     FROM "log" U0
     GROUP BY U0."project_id"
     ORDER BY MAX(U0."date") DESC
     LIMIT 4)
ORDER BY "log"."date" DESC
...