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 &gt; ?", created_at).order(created_at: :asc).first 和您的 previous 方法以包括 self.class.where("created_at &lt; ?", 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 &gt; ?) OR group_id &gt; ? 这样的条件。一个肯定不是那么干净的解决方案...... @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,这很容易,因为所有ids 都不同,Rails Guy's answer 仅描述了这一点:在next 中,对于已知的id,选择具有较大id 的第一个条目(如果结果已排序id,默认情况下)。不仅如此 - 他的回答暗示将 nextprevious 放入模型类。这样做。

如果有多个订单条件,事情就会变得复杂。比如说,我们有一组行首先按group 参数排序(在不同的行上可能有相同的值),然后是id(保证每个地方的id 都不同)。结果按group排序,再按id排序(都是升序),所以我们可能会遇到两种获取下一个的情况元素,它是列表中第一个具有元素的元素,(这么多 that):

有一个相同的group和一个更大的id更大的group

与前一个元素相同:您需要列表中的最后一个

有一个相同的group和一个更小的id 有一个较小的group

那些分别获取 all 下一个和上一个条目。如果您只需要一个,请使用 Rails 的 firstlast(由 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获取上一个和下一个活动记录对象的最佳方法的主要内容,如果未能解决你的问题,请参考以下文章

Rails 记录 VIP 用户活动

如何显示上一个和下一个列表项的详细信息

在 Rails 中,更新记录或创建新记录(如果不存在)的最佳方法是啥?

Rails 4+ 最佳实践:删除父项同时保留子项

对图像进行上采样和下采样的最佳方法

markdown 当活动记录子对象在Rails中自动保存时