Django:按外键返回一个过滤对象
有没有办法让查询结果每个外键只返回一个对象呢?
比如,我想从django_comments中获取最新的评论,但我只想要每个对象的最新评论,也就是说,只返回一个对象的最新评论,排除掉之前的所有评论。我觉得这有点像在django_comments.content_type和django_comments.object_pk上做一个sql的分组。
++补充信息++
我的最终目标是创建一个活跃评论“主题”的列表,按照最近评论的时间来排序,就像标准的讨论板一样,话题是根据最近的活动来列出的。
我想最好的办法是先获取最新的评论,然后根据内容类型和对象主键进行排序或分组,这样每个相关内容对象只返回一条评论(最新的那条)。然后我可以用这条评论获取我需要的所有信息,所以“主题”这个词用得比较宽泛,因为我其实只是抓取一条评论并跟踪它的主键。
这个模型是django_threadedcomments,它在django_comments的基础上增加了一些用于树形结构、子评论和父评论的字段。
视图:
...这会返回所有评论,包括所有父评论的实例
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 %}
再次感谢!
3 个回答
可以考虑把最后一条帖子存储为一个外键,放在某个地方(比如父对象的表里)。每次发消息或删除消息的时候,就更新这个外键。
是的,这样会有重复的数据,但值得考虑。每次请求都要运行复杂的查询(特别是在首页)可能会让你的应用变得很慢。这是一种务实的方法,可以在不影响性能的情况下实现你想要的效果。
谢谢你,Glenn 和 vdboor。我同意,提议的想法会让 SQL 变得太复杂,而且会严重影响性能。
关于 last_comment_id 的建议非常好,但我觉得在我具体的情况下,最好的做法是创建一个单独的“THREAD”模型,这个模型会存储原始对象的内容类型和对象主键,以及该对象最后评论的 ID 和时间戳,还有一些其他信息。这样可以方便地查找内容对象,并按时间顺序过滤查询结果,而且这样做能让后台的处理方式更贴近前端的展示,这对将来来说可能是个好主意。:)
祝好,
jnh
在SQL中,这个事情其实挺复杂的;你可能通过ORM(对象关系映射)也做不到。
你不能用GROUP BY来处理这个问题。GROUP BY是用来告诉SQL如何把数据分组以便进行汇总的,而你这里并不是在做汇总。"SELECT x, y FROM table GROUP BY x" 这样的写法是错误的,因为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触发器来自动处理这个更新。