Вы, наверное, уже поняли, что можете достичь своей цели, используя reorder
. Вот моя теория, почему reorder
работает, а except
нет.
Важно, чтобы такие методы, как order
, where
, except
, обрабатывались экземплярами ActiveRecord::Relation
, в то время как области действия, например. ordered
из вашего примера делегируются экземпляром ActiveRecord::Relation
вашему классу модели.
Метод some_relation.order(:x)
просто возвращает свежую копию some_relation
с добавлением :x
в список order_values
. Аналогично, some_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
И это 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
, он появляется в результате, несмотря на то, что он был очищен от отношения, возвращаемого областью видимости.
Вот список ссылок на соответствующие источники:
person
Serge Balyuk
schedule
04.09.2012