您可能已经想到使用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 就可以按我们的预期工作。
对作用域方法的调用,当作用域实现为 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 的效果。如果我们合并a 和b,并且b 没有设置顺序,但a 有,结果仍然有顺序。现在reorder 用一个技巧来解决它:它在关系上设置特殊标志reorder_flag,它控制merge 如何携带order_values。
这是我的测试样本。我正在使用default_scope 将订单注入Product#scoped。在您的示例中,订单可能通过Pie 中的关联注入到Level#scoped,它可能看起来像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 顺序,尽管它已从范围返回的关系中清除,但它仍出现在结果中。
以下是相关来源的链接列表: