Ste*_*vis 27 ruby join ruby-on-rails has-many-through
我在工作中有一个RoR项目.以下是我的模型的适用部分.
家
has_many :communities, :through => :availabilities
has_many :availabilities, :order => "price ASC"
Run Code Online (Sandbox Code Playgroud)
社区
has_many :homes, :through => :availabilities
has_many :availabilities
Run Code Online (Sandbox Code Playgroud)
可用性
belongs_to :home
belongs_to :community
Run Code Online (Sandbox Code Playgroud)
数据库中的"可用性"表具有附加数据列"价格"
所以现在我可以打电话了
@home.availabilities.each do |a|
a.community.name
a.price
Run Code Online (Sandbox Code Playgroud)
并按我的要求取回按价格订购的可用性数据.我的问题是:
有没有办法自动订购房屋avaliabilities.first.price(第一个=最低)?也许有点什么default_scope :order?
eco*_*gic 34
我建议避免使用default_scope,尤其是在另一张桌子上的价格等方面.每次使用该表时,都会发生加入和排序,可能会在复杂查询中产生奇怪的结果,并且无论如何都会使查询变慢.
它的范围没有任何问题,它更简单,更清晰,你可以简单地说:
scope :ordered, -> { includes(:availabilities).order('availabilities.price') }
Run Code Online (Sandbox Code Playgroud)
PS:记得要添加一个索引 price
Ste*_*vis 22
在这篇相关文章的帮助下弄明白了.
我将订单从Home模型移到了Availability模型中:
可用性
default_scope :order => "price ASC"
Run Code Online (Sandbox Code Playgroud)
然后我急切地将可用性加载到Home模型中并按价格排序:
家
default_scope :include => :availabilities, :order => "availabilities.price ASC"
Run Code Online (Sandbox Code Playgroud)
TeW*_*eWu 11
@ecoologic 答案:
scope :ordered, -> { includes(:availabilities).order('availabilities.price') }
Run Code Online (Sandbox Code Playgroud)
是伟大的,但应该提到includes可能,并在某些情况下应该被替换joins.他们都有最佳用例.
从实际角度来看,有两个主要区别:
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]) }
Run Code Online (Sandbox Code Playgroud)
您还可以指定 ASC 方向
scope :ordered, -> { includes(:availabilities).order(Availability.arel_table[:price].asc) }
Run Code Online (Sandbox Code Playgroud)
DESC:
scope :ordered, -> { includes(:availabilities).order(Availability.arel_table[:price].desc) }
Run Code Online (Sandbox Code Playgroud)
arel_table在ActiveRecord模型上使用可以让您避免表名更改时的情况(但这种情况很少发生)。
请注意,main_table#id为确定排序添加是很好的。
所以最终版本将是:
scope :ordered, -> {
includes(:availabilities).
order(Availability.arel_table[:price].asc, order(Home.arel_table[:id].asc)
}
Run Code Online (Sandbox Code Playgroud)
在Rails 5.2+中,将字符串参数传递给order方法时,您可能会收到弃用警告:
优先警告:危险的查询方法(其参数用作原始SQL的方法)以非属性参数“ table.column”调用。在Rails 6.0中将禁止使用非属性参数。不应使用用户提供的值(例如请求参数或模型属性)来调用此方法。
为了解决这个问题,您可以使用Arel.sql():
scope :ordered, -> {
includes(:availabilities).order(Arel.sql('availabilities.price'))
}
Run Code Online (Sandbox Code Playgroud)
您还可以像这样对链接表进行排序(例如):
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
Run Code Online (Sandbox Code Playgroud)
问候