我正在进行一个 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
的东西?
我建议避免使用
default_scope
,尤其是在另一张桌子上的价格等内容上。每次您使用该表时,都会发生联接和排序,这可能会在复杂的查询中给出奇怪的结果,并且无论如何都会使您的查询变慢。
有自己的作用域没有什么问题,它更简单,也更清晰,你可以把它变得简单:
scope :ordered, -> { includes(:availabilities).order('availabilities.price') }
我称之为
price_ordered
。
PS:记得在
price
上添加索引;另请参阅此处的其他精彩答案,以在 join/include 之间做出决定。
在相关帖子的帮助下解决了这个问题。
我将订购从“家庭”模型移至“可用性”模型中:
可用性
default_scope :order => "price ASC"
然后我急切地将可用性加载到家庭模型中并按价格排序:
首页
default_scope :include => :availabilities, :order => "availabilities.price ASC"
@ecoologic 答案:
scope :ordered, -> { includes(:availabilities).order('availabilities.price') }
很棒,但应该提到
includes
可以,并且在某些情况下应该被 joins
取代。它们都有各自的最佳用例(请参阅:#1、#2)。
从实际角度来看,有两个主要区别:
includes
加载关联记录;在这种情况下 Availability
记录。 joins
不要加载任何关联的记录。因此,当您想使用连接模型中的数据时,您应该使用 includes
,例如在某处显示 price
。另一方面,如果您打算仅在查询中使用连接模型的数据,则应使用 joins
,例如在 ORDER BY
或 WHERE
子句中。
includes
加载所有记录,而joins
仅加载那些具有关联连接模型的记录。因此,在 OP 的情况下,Home.includes(:availabilities)
将加载所有房屋,而 Home.joins(:availabilities)
将仅加载那些至少关联了一个可用性的房屋。
另请参阅这个问题。
实现此目的的另一种方法:
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)
}
在 Rails 5.2+ 中,将字符串参数传递给 order 方法时,您可能会收到弃用警告:
弃用警告:使用非属性参数调用危险的查询方法(参数用作原始 SQL 的方法):“table.column”。 Rails 6.0 中不允许使用非属性参数。不应使用用户提供的值(例如请求参数或模型属性)调用此方法。
要解决这个问题,您可以使用
Arel.sql()
:
scope :ordered, -> {
includes(:availabilities).order(Arel.sql('availabilities.price'))
}
您还可以像这样对链接表进行排序(例如):
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
问候