Rails - 按连接表数据排序
Posted
技术标签:
【中文标题】Rails - 按连接表数据排序【英文标题】:Rails - Sort by join table data 【发布时间】:2012-03-01 03:26:58 【问题描述】:我有一个 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
数据库中的“availabilities”表有额外的数据列“price”
所以现在我可以打电话了
@home.availabilities.each do |a|
a.community.name
a.price
并根据需要取回按价格排序的可用性数据。我的问题是这样的:
有没有办法通过avaliabilities.first.price
(第一=最低)自动订购房屋?也许是default_scope :order
?
【问题讨论】:
【参考方案1】:在this related post 的帮助下解决了这个问题。
我将排序从 Home 模型移到了 Availability 模型中:
可用性
default_scope :order => "price ASC"
然后我急切地将可用性加载到 Home 模型中并按价格排序:
首页
default_scope :include => :availabilities, :order => "availabilities.price ASC"
【讨论】:
【参考方案2】:我建议避免使用default_scope
,尤其是在另一张桌子上的价格之类的东西上。每次您使用该表时,都会进行连接和排序,这可能会在复杂查询中产生奇怪的结果,并且无论如何都会使您的查询变慢。
它自己的范围没有什么问题,它更简单,更清晰,你可以让它变得简单:
scope :ordered, -> includes(:availabilities).order('availabilities.price')
PS:记得在price
上添加索引;另请参阅此处的其他出色答案,以在 join/include 之间做出决定。
【讨论】:
你需要使用includes
而不是joins吗?
视情况而定,@TeWu 在解释他的答案的差异方面做得很好。尤其是 Tom Dallimore 博客的链接非常棒。
@ecoologic ***.com/questions/56669484/…【参考方案3】:
@ecoologicanswer:
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)
将仅加载与至少一个可用性相关联的房屋。
另见this question。
【讨论】:
最佳用例链接现已断开 感谢您的信息。链接已断开,因为链接的博客已死……真可惜。我已经链接到这篇博文的存档版本,还添加了一个链接到一些关于这个主题的非史前博文;)【参考方案4】:在 Rails 5.2+ 中,将字符串参数传递给 order 方法时,您可能会收到弃用警告:
弃用警告:使用非属性参数调用的危险查询方法(其参数用作原始 SQL 的方法):“table.column”。 Rails 6.0 将不允许使用非属性参数。不应使用用户提供的值(例如请求参数或模型属性)调用此方法。
要解决这个问题,你可以使用Arel.sql()
:
scope :ordered, ->
includes(:availabilities).order(Arel.sql('availabilities.price'))
【讨论】:
【参考方案5】:实现此目的的另一种方法:
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)
【讨论】:
【参考方案6】:您还可以像这样对链接表进行排序(例如):
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
问候
【讨论】:
以上是关于Rails - 按连接表数据排序的主要内容,如果未能解决你的问题,请参考以下文章