您可能已经想到可以通过使用reorder. 所以这是我关于为什么reorder有效和except无效的理论。
重要的是,像order, where,之类的方法except由 , 的实例处理ActiveRecord::Relation,而范围(例如ordered来自您的示例)则由 的实例委托ActiveRecord::Relation给您的模型类。
some_relation.order(:x)方法只是返回添加到其列表中的some_relation新副本。同样,将返回with empty的副本。只要调用链包含这样的关系方法,就可以按我们的预期工作。:xorder_valuessome_relation.except(:order)some_relationorder_valuesexcept
对作用域方法的调用,当作用域实现为 lambda 返回关系时,最终会合scoped并由 lambda 返回的关系返回的模型关系:
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如果我们合并aand b, andb没有设置顺序,但是a有,结果仍然有顺序。现在reorder用一个技巧来解决它:它reorder_flag在关系上设置特殊标志来控制如何merge进行order_values。
这是我的测试样本。我default_scope用来将订单注入Product#scoped. Level#scoped在您的示例中,订单可能通过关联 in注入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顺序,尽管它已从范围返回的关系中清除,但它仍出现在结果中。
以下是相关来源的链接列表: