ruby on rails - best way to validate ownership with HMABTM -


i have 3 models this:

class user < activerecord::base   has_many :items   has_many :other_itmes end  class item < activerecord::base   belongs_to :user   has_and_belongs_to_many :other_items    validate :validate_other_item_ownership   def validate_other_item_ownership     if       (user_ids = otheritem.where(id: other_item_ids).pluck(:user_id).uniq).present? &&         (user_ids.size > 1 || user_ids.first != user_id)           errors.add(:other_item_ids, 'other items must belong same user item')     end   end end  class otheritem < activerecord::base   belongs_to :user   has_and_belongs_to_many :items    validate :validate_item_ownership   def validate_item_ownership     if       (user_ids = item.where(id: item_ids).pluck(:user_id).uniq).present? &&         (user_ids.size > 1 || user_ids.first != user_id)           errors.add(:item_ids, 'items must belong same user other item')     end   end end 

and 2 controllers this:

class itemscontroller < applicationcontroller   def update     @item = item.find params[:id]     @item.other_item_ids = params[:item][:other_item_ids] #assignline     @item.save!   end end  class otheritemscontroller < applicationcontroller   def update     @other_item = otheritem.find params[:id]     @other_item.item_ids = params[:other_item][:item_ids] #assignline     @other_item.save!   end end 

the problem activerecord saves items on #assignline, while call #save! correctly raises activerecord::recordinvalid association still persisted.

i want user able link items each other owned.

excellent question! i've got answer ;)

(tldr) ditch has_and_belongs_to_many, create model on top of join table, put validation logic in join model, , use has_many :through.

to demonstrate, let's consider relationship between artist , song, both belonging user. join model , has_many :through we'll have these model classes:

class artist   belongs_to :user   has_many :artist_songs   has_many :songs, through: :artist_songs end  class song   belongs_to :user   has_many :artist_songs   has_many :artists, through: :artist_songs end  class artistsong   belongs_to :artist   belongs_to :song end 

this clean validation logic, add in 1 place:

class artistsong   #...   validate :ownership private   def ownership     unless artist.user_id == song.user_id       errors[:base] << 'artist , song must belong same user'     end   end end 

then in controller:

class artistscontroller   def update     @artist = current_user.artists.find params[:id]     @artist.update artist_params   end private   def artist_params     params.require(:artist).permit(:name, song_ids: [])   end end 

note: looks rails doing save! on join model. means if validation fails (just on artistsong, not artist) raise exception instead of return false. should ever happen malicious users, no worries.

i use habtm. having model join table gives more flexibility. example, add position field , like:

class artist   #...   has_many :artist_songs, order: -> {'position asc'}   #... end 

Comments

Popular posts from this blog

php - Calling a template part from a post -

Firefox SVG shape not printing when it has stroke -

How to mention the localhost in android -