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 %
再次感谢!
【问题讨论】:
基本上,你想要一个 SQLLIMIT 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:每个外键返回一个过滤对象的主要内容,如果未能解决你的问题,请参考以下文章