我正在尝试通过其priority:integer
列管理ActiveRecord模型。我想用after_update
和after_create
挂钩来管理行,以保持他们的订单整洁干净。
我有两个需求:
获取当前项目列表并更新其优先级属性以遵循严格的顺序。 示例:包含所述优先级列的三个项目的列表。
[a.priority = 4, b.priority = 66, c.priority = 92]
变为
[a.priority = 1, b.priority = 2, c.priority = 3]
更新所有行的优先级,以反映在列表中间添加新行。
[a.priority = 1, b.priority = 2, c.priority = 3, d.priority = 4]
添加e.priority = 2
创建新的
[a.priority = 1, e.priority = 2, b.priority = 3, c.priority = 4, d.priority = 5]
github repo:https://github.com/digitalcake/priority_manager
答案 0 :(得分:2)
对于第一种情况,您可以执行类似
的操作Model.order("priority ASC").each_with_index {|m,i|
m.update_attribute(:priority, i+1) }
第二个
Model.where("priority >= ?", new_priority).each {|m|
m.update_attribute(:priority, m + 1) }
那就是说,如果你只对排序而不是列表上的绝对位置感兴趣,那么如果不使用整数来存储你的优先级,你会使用浮点数更有效率。通过在您希望它们介于两者之间的对象的优先级之间分配一个值来插入一行。 IE在 b 和 c 之间插入 a ,各自的优先级 pb 和 pc 分配它的优先级为 pa =( pc + pb )/ 2
这样整体排序保持不变,但每次插入新行时,您都不需要触摸并重新保存每个具有更高优先级的对象。
答案 1 :(得分:0)
我刚刚在我正在构建的其中一个应用中处理完全相同的场景。 接受的答案中的解决方案不起作用,因为它将递归地使用回调来调用您尝试更新的对象(在update_attributes内)。我们还需要在查询中跳过self对象的id。
以下是我最终如何做到这一点,它似乎适用于所有情况。
after_commit :order_priorities, :if => :persisted?
after_destroy :handle_priorities_when_destroyed
def order_priorities
correct_priority = MyModel.where('id != ? AND priority < ?',id,priority).count + 1
MyModel.where.not(id:id).order(:priority).each_with_index {|x,i|
if x.priority < priority
x.update_column(:priority, i+1)
else
x.update_column(:priority, i+2)
end
}
self.update_column(:priority,correct_priority)
end
def handle_priorities_when_destroyed
MyModel.where.not(id:id).order(:priority).each_with_index {|x,i|
x.update_column(:priority, i+1)
}
end
这里我使用的是after_commit回调,这样我就可以为我在我的方法中定义的self设置correct_priority。其他回调将不起作用,因为在提交期间将覆盖该值。
使用update_column,以便在我不需要时跳过回调。
逻辑是自我解释的。