Rails – 按连接表数据排序

我在工作中有一个RoR项目。 以下是我的模型的适用部分。

has_many :communities, :through => :availabilities has_many :availabilities, :order => "price ASC" 

社区

 has_many :homes, :through => :availabilities has_many :availabilities 

可用性

 belongs_to :home belongs_to :community 

数据库中的“可用性”表具有附加数据列“价格”

所以现在我可以打电话了

 @home.availabilities.each do |a| a.community.name a.price 

并按我的要求取回按价格订购的可用性数据。 我的问题是:

有没有办法通过avaliabilities.first.price (第一个=最低)自动订购Homes? 也许是default_scope :order东西default_scope :order

我建议避免使用default_scope ,尤其是在另一个表上的价格之类的东西上。 每次使用该表时,都会进行连接和排序,可能会在复杂查询中产生奇怪的结果,并且无论如何都会使查询变慢。

它的范围没有任何问题,它更简单,更清晰,你可以简单地说:

 scope :ordered, -> { includes(:availabilities).order('availabilities.price') } 

PS:记得在price上添加一个索引

在这篇相关文章的帮助下想出来了。

我将订单从Home模型移到了Availability模型中:

可用性

 default_scope :order => "price ASC" 

然后我急切地将可用性加载到Home模型中并按价格排序:

 default_scope :include => :availabilities, :order => "availabilities.price ASC" 

@ecoologic 答案 :

 scope :ordered, -> { includes(:availabilities).order('availabilities.price') } 

是伟大的,但应该提到includes可能,在某些情况下应该被joins取代。 他们都有自己的最佳用例 。

从实际角度来看,有两个主要区别:

  1. includes相关记录的负载; 在这种情况下Availability记录。 joins不加载任何关联的记录。 因此,当您想要使用来自连接模型的数据时,您应该使用includes ,例如在某处显示price 。 另一方面,如果您打算仅在查询中使用连接模型的数据,例如在ORDER BYWHERE子句中,则应使用连接。

  2. includes加载所有记录,而joins只加载那些具有关联连接模型的记录。 因此,在OP的情况下, Home.includes(:availabilities)将加载所有家庭,而Home.joins(:availabilities)将仅加载至少具有一个可用性的家庭。

另见这个问题 。

在Rails 5.2+中,在将字符串参数传递给order方法时,您可能会收到弃用警告:

DEPRECATION WARNING:使用非属性参数调用的危险查询方法(其参数用作原始SQL的方法):“table.column”。 Rails 6.0中不允许使用非属性参数。 不应使用用户提供的值调用此方法,例如请求参数或模型属性。

要解决此问题,您可以使用Arel.sql()

 scope :ordered, -> { includes(:availabilities).order(Arel.sql('availabilities.price')) } 

另一种实现此目的的方法:

 scope :ordered, -> { includes(:availabilities).order(Availability.arel_table[:price]) } 

您也可以使用指定ASC方向

 scope :ordered, -> { includes(:availabilities).order(Availability.arel_table[:price].asc) } 

DESC

 scope :ordered, -> { includes(:availabilities).order(Availability.arel_table[:price].desc) } 

ActiveRecord模型上使用arel_table可以在表名更改时保存方案(但很少发生)。

请注意,为确定性排序添加main_table#id是很好的。

所以最终版本将是:

 scope :ordered, -> { includes(:availabilities). order(Availability.arel_table[:price].asc, order(Home.arel_table[:id].asc) }