Rails获取上一个和下一个活动记录对象的最佳方法
Posted
技术标签:
【中文标题】Rails获取上一个和下一个活动记录对象的最佳方法【英文标题】:Rails best way to get previous and next active record object 【发布时间】:2014-09-04 12:40:15 【问题描述】:我需要使用 Rails 获取上一个和下一个活动记录对象。我做到了,但不知道这样做是否正确。
我有什么:
控制器:
@product = Product.friendly.find(params[:id])
order_list = Product.select(:id).all.map(&:id)
current_position = order_list.index(@product.id)
@previous_product = @collection.products.find(order_list[current_position - 1]) if order_list[current_position - 1]
@next_product = @collection.products.find(order_list[current_position + 1]) if order_list[current_position + 1]
@previous_product ||= Product.last
@next_product ||= Product.first
product_model.rb
default_scope -> order(:product_sub_group_id => :asc, :id => :asc)
所以,这里的问题是我需要访问我的数据库并获取所有这些 id 以知道谁是前一个和下一个。
尝试使用 gem order_query,但它对我不起作用,我注意到它会进入数据库并按该顺序获取所有记录,所以,这就是为什么我这样做但只获取 id .
我找到的所有解决方案都是简单的订单查询。按 id 或类似优先级字段的方式排序。
【问题讨论】:
您使用的是哪个数据库? PostgreSQL? 【参考方案1】:在您的Product
模型中编写这些方法:
class Product
def next
self.class.where("id > ?", id).first
end
def previous
self.class.where("id < ?", id).last
end
end
现在你可以在你的控制器中做:
@product = Product.friendly.find(params[:id])
@previous_product = @product.next
@next_product = @product.previous
请试一试,但尚未测试。 谢谢
【讨论】:
问题是我不是按ID订购的。 它仍然是放置这些方法的一个很好的例子。可以投入使用。 我同意。在视图中怎么称呼? 我对 Rails Guy 的回答投了赞成票,但想解决关于通过 id 以外的其他东西排序的问题,例如created_at。根据@Mogsdad 和其他人的要求,我将答案更改为评论。要按 created_at 排序,请将建议的next
方法的实现更改为包括:self.class.where("created_at > ?", created_at).order(created_at: :asc).first
和您的 previous
方法以包括 self.class.where("created_at < ?", created_at).order(created_at: :asc).last
您的解决方案帮助了我。如此优雅。【参考方案2】:
我认为只使用两个 SQL 请求会更快,即只选择两行(而不是整个表)。考虑到您的默认顺序是按 id 排序的(否则,强制按 id 排序):
@previous_product = Product.where('id < ?', params[:id]).last
@next_product = Product.where('id > ?', params[:id]).first
如果产品是最后一个,那么@next_product 将为nil,如果它是第一个,那么@previous_product 将为nil。
【讨论】:
@WilliamWeckl 我也以一种不太清楚的方式反映了这一点:您可以形成像(group_id = ? AND id > ?) OR group_id > ?
这样的条件。一个肯定不是那么干净的解决方案......
@WilliamWeckl :是的,postGre 似乎存在 row_number :***.com/questions/3397121/… 不幸的是,我认为您无法通过干净的活动记录方法来处理它。您将不得不使用 Model.find_by_sql(...)。
@D-side 不太干净但仍然很有效!但是当你有这样的嵌套条件时,你不应该以更少的努力从更有机会为真的那个开始,这样你就不必执行第二个了吗? (因此 group_id > ? OR (group_id = ? AND id > ?) )??
@WilliamWeckl 我不知道有任何标准方法可以实现这一目标。由于这只能(可能)手动实现,因此只能在放置代码的位置上遵循 Rails 方式。请参阅有关如何将这些方法打包到模型类中以便于引用的另一个答案。如果你愿意,你甚至可以使用arel
来避免普通的 SQL 条件。
@WilliamWeckl 这只是 Ruby 编写 SQL 的方式。 100% Ruby 更容易维护。【参考方案3】:
没有简单的开箱即用解决方案。
有点脏,但工作方法是仔细整理找到下一个和上一个项目的条件。使用id
,这很容易,因为所有id
s 都不同,Rails Guy's answer 仅描述了这一点:在next
中,对于已知的id
,选择具有较大id
的第一个条目(如果结果已排序id
,默认情况下)。不仅如此 - 他的回答暗示将 next
和 previous
放入模型类。这样做。
如果有多个订单条件,事情就会变得复杂。比如说,我们有一组行首先按group
参数排序(在不同的行上可能有相同的值),然后是id
(保证每个地方的id 都不同)。结果按group
排序,再按id
排序(都是升序),所以我们可能会遇到两种获取下一个的情况元素,它是列表中第一个具有元素的元素,(这么多 that):
group
和一个更大的id
有更大的group
与前一个元素相同:您需要列表中的最后一个
有一个相同的group
和一个更小的id
有一个较小的group
那些分别获取 all 下一个和上一个条目。如果您只需要一个,请使用 Rails 的 first
和 last
(由 Rails Guy 建议)或 limit(1)
(并注意 asc/desc 排序)。
【讨论】:
@glebm 作者明确表示他尝试使用它。尽管如此,order_query
不是开箱即用的解决方案,它是一个第三方库。我理解您的担忧,作为图书馆的维护者,最好与作者澄清一下究竟是什么不起作用。【参考方案4】:
这就是order_query 所做的。请尝试最新版本,如果它不适合您,我可以帮助您:
class Product < ActiveRecord::Base
order_query :my_order,
[:product_sub_group_id, :asc],
[:id, :asc]
default_scope -> my_order
end
@product.my_order(@collection.products).next
@collection.products.my_order_at(@product).next
这会运行一个查询,只加载下一条记录。 Read more on Github.
【讨论】:
以上是关于Rails获取上一个和下一个活动记录对象的最佳方法的主要内容,如果未能解决你的问题,请参考以下文章