Rails - 按连接表数据排序

时间:2012-02-08 16:43:04

标签: ruby-on-rails ruby join has-many-through

我的作品中有一个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(第一个=最低)自动订购房屋?也许有default_scope :order的东西?

6 个答案:

答案 0 :(得分:29)

我建议避免使用default_scope,尤其是在另一张桌子上的价格等方面。每次您使用该表时,都会发生加入和排序,可能会在复杂查询中产生奇怪的结果,并且无论如何都会使查询变慢。

它的范围没有任何问题,它更简单,而且更清晰,你可以把它简单化为:

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

PS:请记得在price

上添加索引

答案 1 :(得分:21)

this related post的帮助下计算出来。

我将订单移出Home模型并进入可用性模型:

<强>状况

default_scope :order => "price ASC"

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

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

答案 2 :(得分:10)

@ecoologic answer

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

很棒,但应该提到includes可以,并且在某些情况下应该由joins替换。 They both have their optimal use cases

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

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

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

  3. 另见this question

答案 3 :(得分:5)

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

  

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

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

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

答案 4 :(得分:3)

另一种实现方法:

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) }

arel_table模型上使用ActiveRecord可以使您避免表名更改时发生的情况(但是这种情况很少发生)。

请注意,最好添加main_table#id用于确定排序。

因此最终版本为:

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

答案 5 :(得分:0)

您还可以像这样对链接表进行排序(例如):

class User
  has_many :posts
end

class Post
  belongs_to :user

  scope :sorted_by_user_and_title, -> { 
    joins(:user).merge(
     User.order(first_name: :asc, last_name: :asc)
    )
    .order(title: :desc)
    # SELECT * FROM `posts`
    # INNER JOIN `users` ON `posts`.`user_id` = `users`.`id`
    # ORDER BY
    # `users`.`first_name` ASC, `users`.`last_name` ASC, `posts`.`title` DESC;
  }
  scope :sorted_by_title_and_user, -> { 
    order(title: :desc)
    .joins(:user).merge(
     User.order(first_name: :asc, last_name: :asc)
    )
    # SELECT * FROM `posts`
    # INNER JOIN `users` ON `posts`.`user_id` = `users`.`id`
    # ORDER BY
    # `posts`.`title` DESC, `users`.`first_name` ASC, `users`.`last_name` ASC;
  }
end

致谢