を使用することで目標を達成できることは、おそらくすでにお気づきでしょうreorder。では、なぜreorder機能し、except機能しないのかについての私の理論を次に示します。
、などorderのメソッドは のインスタンスによって処理されますが、例などのスコープはのインスタンスによってモデル クラスに委任されることが重要です。whereexceptActiveRecord::RelationorderedActiveRecord::Relation
some_relation.order(:x)some_relationメソッドは、 のリストに:x追加されたの新しいコピーを返すだけですorder_values。同様に、with emptysome_relation.except(:order)のコピーを返します。一連の呼び出しがそのようなリレーション メソッドで構成されている限り、期待どおりに機能します。some_relationorder_valuesexcept
スコープがリレーションを返すラムダとして実装されている場合、スコープ メソッドの呼び出しは、scopedラムダによって返されるリレーションとによって返されるモデルのリレーションのマージで終了します。
scopes[name] = lambda do |*args|
options = scope_options.is_a?(Proc) ? scope_options.call(*args) : scope_options
relation = if options.is_a?(Hash)
scoped.apply_finder_options(options)
elsif options
scoped.merge(options) # <- here options is what returned by your :ordered lambda
else
scoped
end
extension ? relation.extending(extension) : relation
end
これは、マージされるリレーションの 1 つに対してのみ行われた場合mergeの効果を保持しません。exceptと をマージするaとb、b順序が設定されていませんが、設定されているa場合でも、結果には順序があります。reorderこれはトリックで回避できます:をどのように運ぶreorder_flagかを制御する特別なフラグをリレーションに設定します。mergeorder_values
これが私のテストサンプルです。私はdefault_scope注文をに注入するために使用していProduct#scopedます。Level#scopedあなたの例では、注文はおそらくの関連付けによって注入されます。Pieこれは のようになりhas_many :levels, :order => 'position'ます。
class Product < ActiveRecord::Base
default_scope order('id DESC')
scope :random_order, lambda {
r = order('random()')
puts "from lambda: " + r.order_values.inspect
r
}
end
# in console:
>> Product.scoped.order_values
=> ["id DESC"]
>> Product.random_order.order_values
from lambda: ["id DESC", "random()"]
=> ["id DESC", "id DESC", "random()"]
# now if I change the first line of lambda to
# r = except(:order).order('random()')
>> Product.random_order.order_values
from lambda: ["random()"]
=> ["id DESC", "random()"]
ご覧のとおり、順序Product.scopedがあるid DESCため、スコープによって返されるリレーションからクリアされたにもかかわらず、結果に表示されます。
関連するソースへのリンクのリストは次のとおりです。