を使用することで目標を達成できることは、おそらくすでにお気づきでしょうreorder
。では、なぜreorder
機能し、except
機能しないのかについての私の理論を次に示します。
、などorder
のメソッドは のインスタンスによって処理されますが、例などのスコープはのインスタンスによってモデル クラスに委任されることが重要です。where
except
ActiveRecord::Relation
ordered
ActiveRecord::Relation
some_relation.order(:x)
some_relation
メソッドは、 のリストに:x
追加されたの新しいコピーを返すだけですorder_values
。同様に、with emptysome_relation.except(:order)
のコピーを返します。一連の呼び出しがそのようなリレーション メソッドで構成されている限り、期待どおりに機能します。some_relation
order_values
except
スコープがリレーションを返すラムダとして実装されている場合、スコープ メソッドの呼び出しは、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
かを制御する特別なフラグをリレーションに設定します。merge
order_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
ため、スコープによって返されるリレーションからクリアされたにもかかわらず、結果に表示されます。
関連するソースへのリンクのリストは次のとおりです。