Rails获取上一个和下一个活动记录对象的最佳方式

时间:2014-09-04 12:40:16

标签: ruby-on-rails activerecord

我需要使用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,但它对我不起作用,我注意到它会进入数据库并按顺序获取所有记录,因此,这就是为什么我这样做了同样但只得到ids。

我找到的所有解决方案都是简单的订单查询。按ID排序或类似优先级字段。

4 个答案:

答案 0 :(得分:29)

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

请尝试一下,但未经过测试。 感谢

答案 1 :(得分:4)

我认为只用两个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。

答案 2 :(得分:2)

没有简单易用的解决方案。

有点脏,但工作方式是仔细挑选出找到下一个和上一个项目的条件。使用id非常简单,因为所有id都不同,而Rails Guy's answer只是说明了这一点:next对于已知的id选择第一个条目id较大(如果结果按id排序,则按默认值排序)。更重要的是 - 他的回答提示将nextprevious放入模型类中。这样做。

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

  • 相同group 更大id
  • 更大group

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

  • 相同group 较小id
  • 较小的group

分别获取所有下一个和前一个条目。如果您只需要一个,请使用Rails&#39; firstlast(由Rails Guy建议)或limit(1)(并警惕 asc / desc 排序)。

答案 3 :(得分:2)

这是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