• Rails: Unique constraint and acts_as_list

Here’s a quick tip to fix issues with unique constraints and acts_as_list gem. The issue stems from the moving model’s position not being set to NULL during the move.

To make it work, you need to modify the handling of the model by acts_as_list. It’s not pretty, but it does the job.

# app/models/my_model.rb
class MyModel < ActiveRecord::Base
  validates_uniqueness_of :position

  private
    # Keep the unique constraint that acts_as_list breaks during decrement
    def decrement_positions_on_lower_items(position=nil)
      return unless in_list?
      position ||= send(position_column).to_i
      acts_as_list_class.unscoped.where(
        "#{scope_condition} AND #{position_column} > #{position}"
      ).order(position_column).update_all(
        "#{position_column} = (#{position_column} - 1)"
      )
    end

    # Keep the unique constraint that acts_as_list breaks during insert at
    def insert_at_position(position)
      if in_list?
        old_position = send(position_column).to_i
        new_pos = position
        self.position = nil # Set the position to nil
        self.save
        position = new_pos
        return if position == old_position
        shuffle_positions_on_intermediate_items(old_position, position)
      else
        raise position.to_s
        increment_positions_on_lower_items(position)
      end
      set_list_position(position)
    end
end

And now it will work.

The Author

Dan Schultzer is an active experienced entrepreneur, starting the Being Proactive groups, Dream Conception organization, among other things. You can find him at twitter

Like this post? More from Dan Schultzer

Comments? We would love to hear from you, write us at @dreamconception.