作業中の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
で何か?
特に別のテーブルの価格などでは、default_scope
を使用しないことをお勧めします。そのテーブルを使用するたびに、結合と順序付けが行われ、複雑なクエリで奇妙な結果が得られたり、クエリが遅くなったりする可能性があります。
それ自体のスコープに問題はありません。より単純で、さらに明確です。次のように単純にすることができます。
scope :ordered, -> { includes(:availabilities).order('availabilities.price') }
PS:price
にインデックスを追加することを忘れないでください。 join/includeを決定するには、ここにある他のすばらしい回答も参照してください。
この関連する投稿 の助けを借りてそれを理解しました。
注文をホームモデルから可用性モデルに移動しました。
在庫状況
default_scope :order => "price ASC"
次に、可用性をHomeモデルにロードし、価格で並べ替えました。
ホーム
default_scope :include => :availabilities, :order => "availabilities.price ASC"
@ecoologic answer :
_scope :ordered, -> { includes(:availabilities).order('availabilities.price') }
_
素晴らしいですが、includes
は可能であり、場合によってはjoins
に置き換える必要があることに言及してください。 どちらにも最適な使用例があります 。
実用的な観点から見ると、2つの主な違いがあります。
includes
は関連するレコードをロードします。この場合はAvailability
レコードです。 joins
関連するレコードをロードしません。したがって、結合モデルのデータを使用する場合は、includes
を使用する必要があります。どこかにprice
を表示します。一方、結合モデルのデータをクエリでのみ使用する場合は、joins
を使用する必要があります。 _ORDER BY
_またはWHERE
句内。
includes
はすべてのレコードをロードしますが、joins
は結合モデルに関連付けられているレコードのみをロードします。したがって、OPの場合、Home.includes(:availabilities)
はすべてのホームをロードし、Home.joins(:availabilities)
は少なくとも1つの可用性に関連付けられているホームのみをロードします。
この質問 も参照してください。
Rails 5.2+)では、文字列パラメーターを注文メソッドに渡すと、非推奨の警告が表示される場合があります。
非推奨の警告:非属性の引数で呼び出された危険なクエリメソッド(引数が生のSQLとして使用されるメソッド): "table.column"。 Rails 6.0では、属性以外の引数は許可されません。このメソッドは、リクエストパラメータやモデル属性などのユーザー指定の値で呼び出すことはできません。
これを解決するには、Arel.sql()
を使用できます。
scope :ordered, -> {
includes(:availabilities).order(Arel.sql('availabilities.price'))
}
これを達成する別の方法:
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)
}
次のようにリンクテーブルを並べ替えることもできます(例):
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
よろしく