Django ORM将queryset限制为仅返回数据子集

时间:2012-03-01 07:54:47

标签: python django postgresql django-orm

我在Django应用程序中有以下查询。用户字段是外键。结果可能包含1000个MyModel对象,但仅适用于少数用户。我想将它限制为查询的user__in=部分中每个用户返回的5个MyModel对象。我最终应该使用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 个答案:

答案 0 :(得分:2)

这就是最终工作,并允许我只选择少数用户或所有用户(通过删除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

答案 1 :(得分:-1)

我认为这就是你在寻找的地方(我没有在其他帖子中看到它):

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个元素可能会更改。