1

我正在尝试通过其priority:integer列管理 ActiveRecord 模型。我想用after_updateafter_create钩子来管理行,以保持它们的顺序整洁。

我有两个需求:

  1. 获取当前项目列表并更新其优先级属性以遵循严格的顺序。示例:具有所述优先级列的三个项目的列表。

    [a.priority = 4, b.priority = 66, c.priority = 92]
    

    变成

    [a.priority = 1, b.priority = 2, c.priority = 3]
    
  2. 更新所有行的优先级以反映在列表中间添加新行。

    [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 仓库:https ://github.com/digitalcake/priority_manager

4

2 回答 2

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) }

That said, if you are only interested in the ordering and not the absolute position on the list it would be more efficient if instead of using integers to store your priorities, you used floats. Insert a row by assigning a it value between the priorities of the objects you want it to be in between. IE to insert a between b and c with respective priorities pb and pc assign it a priority of pa = (pc + pb) / 2

This way the overall ordering remains intact, but you dont need to touch and resave every object with a higher priority every time you insert a new row.

于 2013-02-16T02:43:06.377 回答
0

I just handled the exact same scenario in one of the apps I am building. The solution in the accepted answer did not work as it will recursively use callbacks to call the object you are trying to update (inside update_attributes). Also we need to skip the id for self object inside the query.

Here is how I am doing it finally, and it does seem to work for all cases.

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

Here I am using after_commit callback, so that I will be able to set correct_priority for self that I define in my method. Other callbacks will not work as the value will be overwritten during commit.

Using update_column, so that callbacks are skipped when I do not want them.

Logic is self explanatory.

于 2016-03-01T04:42:00.250 回答