【问题标题】:Django ORM limiting queryset to only return a subset of dataDjango ORM 限制查询集只返回数据的子集
【发布时间】:2012-03-01 07:54:46
【问题描述】:

我在 Django 应用程序中有以下查询。用户字段是外键。结果可能包含 1000 个 MyModel 对象,但仅限于少数用户。我想将其限制为在查询的 user__in= 部分中为每个用户返回 5 个 MyModel 对象。我应该得到 5*#users 或更少的 MyModel 对象。

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

通过 ORM 或 SQL(使用 Postgres)都是可以接受的。

谢谢

编辑 2

找到了一种更简单的方法来完成这项工作,我已将其添加为下面的答案。

编辑

cmets 中提到的一些链接有一些很好的信息,尽管没有一个真正适用于 Postgres 或 Django ORM。对于将来寻找此信息的其他人,我在其他问题/回答中对代码的改编在这里。

要实现这是 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 个。

【问题讨论】:

标签: python django postgresql django-orm


【解决方案1】:

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

【讨论】:

    【解决方案2】:

    我认为这就是您要寻找的东西(我在其他帖子中没有看到):

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

    【讨论】:

    • -1,总共会给出10个对象,任务是为每个用户获取5个或更少的对象。
    • 我知道我没有直接回答他的问题。这更像是一个评论,而不是一个完整的答案。但由于我的声誉低,我无法添加评论。我编辑了我的答案,以便更具体地说明他的问题......对不起,我的错误!下次我会更加小心......
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-07-25
    • 1970-01-01
    • 2021-10-21
    • 1970-01-01
    • 2012-03-28
    • 1970-01-01
    • 2013-11-22
    相关资源
    最近更新 更多