brendon / acts_as_list

An ActiveRecord plugin for managing lists.
http://brendon.github.io/acts_as_list/
MIT License
2.05k stars 356 forks source link

Can we don't use remove_from_list when destroy a lot objects? #276

Closed cloudy9101 closed 7 years ago

cloudy9101 commented 7 years ago

In my case, a many to many relationship between user and project through access table, and user can custom order his projects. So I use acts_as_list in the middle table access. But when a project is destroy, I want destroy all the accesses in the project. Because access use acts_as_list, I need invoke remove_from_list with each access. When a project has a lot accesses, it's very slow. Is there any solution for this? Thanks a lot.

swanandp commented 7 years ago

You can use dependent :destroy on your relationship model.

e.g.


class User < ActiveRecord::Base
  has_many :user_projects, dependent: :destroy
  has_many :projects, through: :user_projecs
end

class Project < ActiveRecord::Base
  has_many :user_projects, dependent: :destroy
  has_many :users, through: :user_projecs
end

class UserProject < ActiveRecord::Base
  belongs_to :user
  belongs_to :project
end
swanandp commented 7 years ago

This way, when you delete project, user remains, but their association is destroyed.

brendon commented 7 years ago

Haha! You're quick off the mark today @swanandp! :)

swanandp commented 7 years ago

Oh the power ( or peril ) of notifications!

cloudy9101 commented 7 years ago

I know, but when there are 1000 user_projects, we will execute the 'update position - 1' sql for 1000 times. I think it's too slow. Is that right? 😞

brendon commented 7 years ago

@cloudy9101, are you using the latest version of acts_as_list? We try to detect a destroy via a parent relationship and skip those updates given all the children in the scope get deleted anyway. You need to use dependent: :destroy though.

cloudy9101 commented 7 years ago

@swanandp @brendon Thanks for the response. I saw the code. But because the item is belongs_to two models, and destroy the model which is not the list, I think this will make the list's position discontinuous like [0, 1, 3, 4, 5], and when use insert_at, it will make some mistake, because the param use with insert_at is come from frontend, I'm not sure that you could understand what I am saying. If you can't understand, I could show some use case.

brendon commented 7 years ago

Thanks @cloudy9101, some code would be good. Show us a simplified set of models and the methods you're trying to execute against them.

cloudy9101 commented 7 years ago

Just like the example @swanandp shows, I make some changes.

class User < ActiveRecord::Base
  has_many :user_projects, dependent: :destroy
  has_many :projects, through: :user_projecs
end

class Project < ActiveRecord::Base
  has_many :user_projects, dependent: :destroy
  has_many :users, through: :user_projecs
end

class UserProject < ActiveRecord::Base
  acts_as_list scope: :user

  belongs_to :user
  belongs_to :project
end

# project has_many users
# this code will execute remove_from_list many times(according to the project.users.count)
project.destroy

Sometimes the project.users.count is very large, it will be very slow. Is the use case too odd? 🤣

brendon commented 7 years ago

Unfortunately I think it's the only way if you want the positions to remain consistent (without gaps). Perhaps you could offload it to a delayed_job or something like that?

cloudy9101 commented 7 years ago

OK, I will consider it. Thanks a lot. I will close the issues.

brendon commented 7 years ago

Thanks @cloudy9101, sorry I couldn't be of more help.