Django ORM ограничивающий набор запросов, чтобы вернуть только подмножество данных - PullRequest
1 голос
/ 01 марта 2012

У меня есть следующий запрос в приложении Django.Пользовательское поле является внешним ключом.Результаты могут содержать 1000 объектов MyModel, но только для нескольких пользователей.Я хотел бы ограничить его 5 объектами MyModel, возвращаемыми на пользователя в части запроса user__in=.У меня должно быть 5 * # пользователей или меньше объектов MyModel.

lfs = MyModel.objects.filter(
    user__in=[some,users,here,],
    active=True,
    follow=True,
)

Либо через ORM или SQL (с использованием Postgres) будет приемлемо.

Спасибо

РЕДАКТИРОВАТЬ 2

Найден более простой способ сделать это, который я добавил в качестве ответа ниже.

РЕДАКТИРОВАТЬ

Некоторые ссылки, упомянутые в комментариях, содержали некоторую полезную информацию, хотя ни одна из них не работала с Postgres или Django ORM.Для тех, кто ищет эту информацию в будущем, моя адаптация кода в этих других вопросах / asnwers находится здесь.

Чтобы реализовать это в postgres 9.1, мне пришлось создать пару функций с использованием pgperl (что также требоваломне установить pgperl)

CREATE OR REPLACE FUNCTION set_int_var(name text, val bigint) RETURNS bigint AS $$
    if ($_SHARED{$_[0]} = $_[1]) {
        return $_[1];
    } else {
        return $_[1];
    }
$$ LANGUAGE plperl;

CREATE OR REPLACE FUNCTION get_int_var(name text) RETURNS bigint AS $$
    return $_SHARED{$_[0]};
$$ LANGUAGE plperl;

И мой последний запрос выглядит примерно так:

SELECT x.id, x.ranking, x.active, x.follow, x.user_id
FROM (
    SELECT tbl.id, tbl.active, tbl.follow, tbl.user_id,
           CASE WHEN get_int_var('user_id') != tbl.user_id
THEN
    set_int_var('rownum', 1)
ELSE
    set_int_var('rownum', get_int_var('rownum') + 1)
END AS
    ranking,
set_int_var('user_id', tbl.user_id)
FROM my_table AS tbl
WHERE tbl.active = TRUE AND tbl.follow=TRUE
ORDER BY tbl.user_id
) AS x
WHERE x.ranking <= 5
ORDER BY x.user_id
LIMIT 50

Единственным недостатком этого является то, что если я пытаюсь ограничить пользователей, которых он ищетс помощью user_id IN () все разрушается и возвращается только каждая строка, а не 5 на пользователя.

Ответы [ 2 ]

2 голосов
/ 02 марта 2012

Это то, что сработало и позволило мне выбрать только несколько пользователей или всех пользователей (удалив строку AND mt.user_id IN ()).

SELECT * FROM mytable
WHERE (id, user_id, follow, active) IN (
    SELECT id, likeable, user_id, follow, active FROM mytable mt
    WHERE mt.user_id = mytable.user_id
    AND mt.user_id IN (1, 2)
    ORDER BY user_id LIMIT 5)
ORDER BY likeable
0 голосов
/ 01 марта 2012

Я думаю, это то, что вы искали (я не видел этого в других сообщениях):

https://docs.djangoproject.com/en/dev/topics/db/queries/#limiting-querysets

В других примерах они передаются из набора запросов в список раньше.«нарезка».Если вы сделаете что-то вроде этого (например):

    lfs = MyModel.objects.filter(
        user__in=[some,users,here,],
        active=True,
        follow=True,
    )[:10]

результирующий SQL это запрос с LIMIT 10. в его предложениях.

Итак, запрос, который вы ищете, будет чем-токак это:

mymodel_ids = []
for user in users:
    mymodel_5ids_for_user = (MyModel.objects.filter(
        user=user,
        active=True,
        follow=True,
    )[:5]).values_list('id', flat=True)

    mymodel_ids.extend(mymodel_5ids_for_user)

lfs = MyModel.objects.filter(id__in=mymodel_ids)

имея в lfs объекты MyModel, которые вы ищете (5 записей на пользователя).

Я думаю, что количество запросов, по крайней мере, один на пользователяи один для извлечения всех объектов MyModel с этим фильтром.

Помните о порядке, в котором вы хотите фильтровать объекты.Если вы измените порядок запроса «mymodel_5ids_for_user», первые 5 элементов запроса могут измениться.

...