Django: фильтрация аннотированных результатов - PullRequest
0 голосов
/ 07 февраля 2011

У меня есть две модели:

class Status(models.Model):
    title = models.CharField(max_length=32)

class Task(models.Model):
    user = models.ForeignKey(User)
    status = models.ForeignKey(Status, default=1)
    title = models.CharField(max_length=128)

Я хочу создать список навигации, который содержит все статусы, которые есть в моей модели состояния, например: Сегодня, Завтра, Ожидание, Запланировано, Корзина

Достаточно просто.Затем я хочу отобразить количество задач, назначенных каждому статусу, благодаря SO, также простое:

Status.objects.all().annotate(Count('task'))

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

Сегодня (1) Завтра (1) Ожидание (0) Запланировано (2) Корзина (7)

Теперь хитрость в том, как фильтроватьвышеуказанные значения, чтобы они отражали только текущий вошедший в систему пользователь.Добавление фильтра в набор запросов, по-видимому, удаляет любые нулевые состояния, что имеет смысл.Я хочу эти нулевые статусы, хотя.Моя текущая идея включает Q():

Status.objects.filter(Q(task__user=1) | Q(task__user__isnull=True)).annotate(Count('task'))

Это не работает.

Есть идеи?

Редактировать для Yuji

Status.objects.all().annotate(Count('task'))

Дает:

Входящие (3) Сегодня (0) Следующие (1) Ожидание (0) Запланировано (1) Позднее (0) Когда-нибудь (0) Архив (0) Корзина (0)

Отлично, но 1 из этих задач входящих и запланированных для другого пользователя.Итак, давайте попробуем выполнить фильтрацию.

Status.objects.filter(task__user=current_user).annotate(Count('task'))

Входящие (2) Далее (1)

Работает!Сорта .... Мои (как я их назвал) статусы с нулевым нулем отсутствуют.Я должен сказать, что любой статус, с которым не связана задача, связанная с тем, что в данный момент вошел в систему, не отображается.Я хочу, чтобы он появился.

Ответы [ 3 ]

0 голосов
/ 07 февраля 2011

Работает ли Status.objects.annotate(Count('task')).filter(task__user=current_user)?

На уровне SQL вам нужно LEFT JOIN или что-то подобное, с таблицей Status слева, а не INNER JOIN.Не уверен, как это взаимодействует с аннотациями.

Грубая сила равна [stat.filter(task__user=current_user).count() for stat in Status.objects.all()], если все остальное терпит неудачу, но это N запросов вместо одного.

0 голосов
/ 07 февраля 2011

Ну ... единственный способ, которым я думаю, что вы можете достичь этого, это LEFT OUTER JOIN с или SUBQUERY с ... Я понятия не имею, как выразить левые внешние соединения в django, поэтому я пойду с SUBQUERY путь. Следующее будет использовать extra с некоторым SQL, созданным вручную, наслаждайтесь!

# you should have Task and Status imported
x = Status.objects.extra(select = {
    "task__count" : "SELECT COUNT(*) FROM %(task_table)s WHERE %(task_table)s.%(task_status_id)s = %(status_table)s.%(status_pk)s AND %(task_table)s.%(user_col)s = %(user_id)d" % 
    {
        "task_table" : Task._meta.db_table,
        "task_status_id" : Task._meta.get_field_by_name("status")[0].column,
        "status_table" : Status._meta.db_table,
        "status_pk" : Status._meta.pk.column,
        "user_col" : Task._meta.get_field_by_name("user")[0].column,
        "user_id" : 1

    }
})

Обратите внимание, что я использую много ... недокументированных функций (например, Task._meta), они могут сломаться в будущем (будем надеяться, что нет) ... но, эй, они делают свою работу.

0 голосов
/ 07 февраля 2011

Это должно дать вам количество всех объектов задач, принадлежащих пользователю по количеству.

Status.objects.filter(task__user=current_user).annotate(Count('task'))

Что вы имеете в виду, добавление фильтра удаляет все нулевые статусы?

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

...