Rails - 按连接表数据排序

问题描述 投票:0回答:6

我正在进行一个 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

数据库中的“availities”表有附加数据列“price”

现在我可以打电话了

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

并按照我的要求取回按价格排序的可用性数据。我的问题是这样的:

有没有办法自动按

avaliabilities.first.price
(第一个=最低)排序房屋?也许有
default_scope :order
的东西?

ruby-on-rails ruby join has-many-through
6个回答
42
投票

我建议避免使用

default_scope
,尤其是在另一张桌子上的价格等内容上。每次您使用该表时,都会发生联接和排序,这可能会在复杂的查询中给出奇怪的结果,并且无论如何都会使您的查询变慢。

有自己的作用域没有什么问题,它更简单,也更清晰,你可以把它变得简单:

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

我称之为

price_ordered

PS:记得在

price
上添加索引;另请参阅此处的其他精彩答案,以在 join/include 之间做出决定。


23
投票

相关帖子的帮助下解决了这个问题。

我将订购从“家庭”模型移至“可用性”模型中:

可用性

default_scope :order => "price ASC"

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

首页

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

16
投票

@ecoologic 答案

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

很棒,但应该提到

includes
可以,并且在某些情况下应该被
joins
取代。它们都有各自的最佳用例(请参阅:#1#2)。

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

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

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

另请参阅这个问题


11
投票

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

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

7
投票

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

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

要解决这个问题,您可以使用

Arel.sql()

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

5
投票

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

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

问候

© www.soinside.com 2019 - 2024. All rights reserved.