如何对来自多个模型的记录进行分页? (我需要多态连接吗?)

Posted

技术标签:

【中文标题】如何对来自多个模型的记录进行分页? (我需要多态连接吗?)【英文标题】:how to paginate records from multiple models? (do I need a polymorphic join?) 【发布时间】:2011-12-18 22:56:34 【问题描述】:

经过相当多的搜索,我还是有点迷茫。还有一些其他类似的问题涉及对多个模型进行分页,但它们要么没有答案,要么分别对每个模型进行分页。

我需要一次对帐户的所有记录进行分页。

class Account
  :has_many :emails
  :has_many :tasks
  :has_many :notes
end

所以,我想找到 30 个最近的“事物”,无论它们是什么。使用当前的分页解决方案,这甚至可能吗?

喜欢使用急切加载和 Kaminari 或 will_paginate 的某种组合?


或者,我应该首先设置所有这些东西的多态连接,称为项目。然后对最近的 30 个项目进行分页,然后查找这些项目的关联记录。

如果是这样,我不确定该代码应该是什么样子。有什么建议吗?


哪种方式更好? (甚至可能)

Rails 3.1、Ruby 1.9.2、应用未投入生产。

【问题讨论】:

使用 will_paginate,这应该会有所帮助:***.com/questions/1465949/… 谢谢。但是,这不是我想要的。 认为“对一组数据行进行分页”而不是对一个数据库表的多行进行分页可能会有所帮助。数据来自多少个模型并不重要。您可能还希望查看 kaminiri 以了解它是否更好地满足您的需求。 谢谢@MichaelDurrant,但我不确定如何“通过一组数据行”进行分页。我查看了 Kaminari,但无法确定它是否同时适用于多个表。我想我想知道如何当它可以是模型的任意组合时,你怎么能用 SQL 获取 30 个最新的。绝对没有 sql 专家,但是有没有办法在没有某种 n+1 查询的情况下将查询限制为 30? 【参考方案1】:

好问题...我不确定“好的”解决方案,但您可以在 ruby​​ 中做一个 hacky 解决方案:

您需要先取出每种“事物”中最新的 30 个,并将它们放入一个数组中,以 created_at 为索引,然后按 created_at 对该数组进行排序并取出前 30 个。

完全未重构的开始可能是这样的:

emails = Account.emails.all(:limit => 30, :order => :created_at)
tasks = Account.tasks.all(:limit => 30, :order => :created_at)
notes = Account.notes.all(:limit => 30, :order => :created_at)
thing_array = (emails + tasks + notes).map |thing| [thing.created_at, thing] 
# sort by the first item of each array (== the date)
thing_array_sorted = thing_array.sort_by |a,b| a[0] <=> b[0] 
# then just grab the top thirty
things_to_show = thing_array_sorted.slice(0,30)

注意:未经测试,可能充满了错误... ;)

【讨论】:

感谢您的回复。但是,这样做的性能太差了,每次都必须获取 60 条额外的记录。然后必须跟踪每个呈现了多少。此外,您的 things_array 可以重构为:(emails + tasks + notes).sort_by(&amp;:updated_at).take(30) 需要反向获取最新的:(emails + tasks + notes).sort_by(&amp;:updated_at).reverse.take(30) 是的 - 绝对性能不佳......正如我所说 - 只是一个 hacky 解决方案 :)【参考方案2】:

与 will_paginate :

@records = #do your work and fetch array of records you want to paginate ( various types )

然后执行以下操作:

current_page = params[:page] || 1
per_page = 10
@records = WillPaginate::Collection.create(current_page, per_page, records.size) do   |pager|
pager.replace(@records)
end

那么在你看来:

<%=will_paginate @records%>

【讨论】:

【参考方案3】:
emails = account.emails
tasks = account.tasks
notes = account.notes

@records = [emails + tasks + notes].flatten.sort_by(&:updated_at).reverse

@records = WillPaginate::Collection.create(params[:page] || 1, 30, @records.size) do |pager|
  pager.replace(@records)
end

就是这样... :)

【讨论】:

以上是关于如何对来自多个模型的记录进行分页? (我需要多态连接吗?)的主要内容,如果未能解决你的问题,请参考以下文章

如何对数据表中超过 2000 条记录进行分页? MVC

如何对来自 PowerQuery 的 OData 中的大型数据集的请求进行分页?

如何从多个模型中对数据进行排序/排序?

没有模型 APIView 的 DRF 分页自定义响应

如何使用 Laravel API 资源对加载的关系进行分页

在php中如何对多条记录进行分页