Django:每个外键返回一个过滤对象

Posted

技术标签:

【中文标题】Django:每个外键返回一个过滤对象【英文标题】:Django: return one filtered object per foreign key 【发布时间】:2011-03-06 01:14:30 【问题描述】:

是否可以返回每个外键只返回一个对象的查询集?

例如,我想从 django_cmets 获取最新的 cmets,但我只希望每个对象有一个评论(最新评论),即只返回一个对象的最新评论并排除该对象上所有过去的 cmets .我猜这类似于 django_cmets.content_type 和 django_cmets.object_pk 上的 sql group_by。

++添加信息++

最终目标是创建一个活动评论“主题”列表,按该主题的最新评论显示/排序,就像您的标准讨论板的主题按近期活动列出一样。

我认为最好的方法是获取最新的 cmets,然后按内容类型和 object_pk 对它们进行排序或分组,以便每个相关内容对象只返回一个评论(最新的)。然后我可以使用该评论来获取我需要的所有信息,因此线程一词的使用很松散,因为我实际上只是在获取评论并遵循它的 pk。

模型是 django_threadedcmets,它扩展了 django_cmets,为树、子和父添加了一些字段。

查看:

...这将返回所有 cmets,包括 parent 的所有实例

comments = ThreadedComment.objects.all().exclude(is_public='0').order_by("-submit_date")

...这是理想的

comments = ThreadedComment.objects.all().exclude(is_public='0').order_by("submit_date").[plus sorting logic to exclude multiple instances of the same object_pk and content_type]

模板:

% for comment in comments %

TITLE: comment.content_object.title

STARTED BY : comment.content_object.user

MOST RECENT REPLY : comment.user on comment.submit_date

% endfor %

再次感谢!

【问题讨论】:

基本上,你想要一个 SQL LIMIT 1[0] 用于 Django 查询),但你想要每个线程?您能否向我们展示您的“获取所有 cmets”Django 查询是什么样的,以便我们向您展示如何更改它? 我已经更新了我的问题,谢谢! 使用不同的。看看这个答案***.com/a/14293530/632182 【参考方案1】:

考虑将最后一篇文章作为外键存储在某处(例如,在父对象表中)。每次发布或删除消息时,更新此密钥。

是的,这是重复的,但值得考虑。必须为每个请求(尤其是索引页面)运行复杂的查询可能会降低您的应用程序性能。这是在不损失性能的情况下获得预期效果的务实方法。

【讨论】:

【参考方案2】:

感谢 Glenn 和 vdboor。同意,提议的想法创造了许多 sql 复杂性的方法,并将严重影响性能。

last_comment_id 建议非常好,但我相信对于我的特殊情况,最好的办法是创建一个单独的“线程”模型,该模型存储评论的原始对象的 content_type 和 object_pk 以及 id 和时间戳对象的最后评论,以及其他一些内容。这将允许简单的内容对象查找和按时间顺序过滤的查询集,并使幕后发生的事情更接近于前端演示,这对于后代来说可能是一个好主意。 :)

干杯,

jnh

【讨论】:

【参考方案3】:

这在 SQL 中是一件相当困难的事情;您可能无法通过 ORM 完成。

您不能为此使用 GROUP BY。这用于告诉 SQL 如何对项目进行分组以进行聚合,这不是您在这里所做的。 “SELECT x, y FROM table GROUP BY x”是非法SQL,因为y的值没有意义。

让我们带着清晰的架构来看看这个:

CREATE TABLE objects ( id INTEGER PRIMARY KEY, name VARCHAR );
CREATE TABLE comments ( object_id INTEGER REFERENCES objects (id), text VARCHAR NOT NULL, date TIMESTAMP NOT NULL );

INSERT INTO objects (id, name) VALUES (1, 'object 1'), (2, 'object 2');
INSERT INTO comments (object_id, text, date) VALUES
   (1, 'object 1 comment 1', '2010-01-02'),
   (1, 'object 1 comment 2', '2010-01-05'),
   (2, 'object 2 comment 1', '2010-01-08'),
   (2, 'object 2 comment 2', '2010-01-09');

SELECT * FROM objects o JOIN comments c ON (o.id = c.object_id);

我见过的最优雅的方法是 Postgresql 8.4 的窗口函数。

SELECT * FROM (
    SELECT
        o.*, c.*,
        rank() OVER (PARTITION BY object_id ORDER BY date DESC) AS r
    FROM objects o JOIN comments c ON (o.id = c.object_id)
) AS s
WHERE r = 1;

这将按日期为每个对象选择第一条评论,最新的在前。如果您看不到这是在做什么,请自行执行内部 SELECT 并观察它如何生成 rank(),这非常简单。

我知道使用 Postgresql 执行此操作的其他方法,但我不知道如何在其他数据库中执行此操作。

尝试动态计算它可能会让您非常头疼——而且要使这些复杂的查询表现良好也需要做更多的工作。您最好以简单的方式执行此操作:为每个对象存储一个 last_comment_id 字段,并在添加或删除评论时更新它,这样您就可以加入和排序。您可能可以使用 SQL 触发器来自动处理此更新。

【讨论】:

以上是关于Django:每个外键返回一个过滤对象的主要内容,如果未能解决你的问题,请参考以下文章

Django 多注解返回错误结果

Django过滤同一外键对象的两个字段

根据外部引用的数量过滤 django 对象

在 django 模板中过滤外键对象

Django过滤多个外键关系

为什么在Django上使用bulk_create插入带有外键的数据会返回“属性对象不可调用”?