Rails idiom to avoid duplicates in has_many :through

前端 未结 8 1839
难免孤独
难免孤独 2020-12-07 12:09

I have a standard many-to-many relationship between users and roles in my Rails app:

class User < ActiveRecord::Base
  has_many :user_roles
  has_many :ro         


        
相关标签:
8条回答
  • 2020-12-07 13:04

    Use Array's |= Join Method.

    You can use Array's |= join method to add an element to the Array, unless it is already present. Just make sure you wrap the element in an Array.

    role                  #=> #<Role id: 1, name: "1">
    
    user.roles            #=> []
    
    user.roles |= [role]  #=> [#<Role id: 1, name: "1">]
    
    user.roles |= [role]  #=> [#<Role id: 1, name: "1">]
    

    Can also be used for adding multiple elements that may or may not already be present:

    role1                         #=> #<Role id: 1, name: "1">
    role2                         #=> #<Role id: 2, name: "2">
    
    user.roles                    #=> [#<Role id: 1, name: "1">]
    
    user.roles |= [role1, role2]  #=> [#<Role id: 1, name: "1">, #<Role id: 2, name: "2">]
    
    user.roles |= [role1, role2]  #=> [#<Role id: 1, name: "1">, #<Role id: 2, name: "2">]
    

    Found this technique on this StackOverflow answer.

    0 讨论(0)
  • 2020-12-07 13:10

    I ran into this today and ended up using #replace, which "will perform a diff and delete/add only records that have changed".

    Therefore, you need to pass the union of the existing roles (so they don't get deleted) and your new role(s):

    new_roles = [role]
    user.roles.replace(user.roles | new_roles)
    

    It's important to note that both this answer and the accepted one are loading the associated roles objects into memory in order to perform the Array diff (-) and union (|). This could lead to performance issues if you're dealing with a large number of associated records.

    If that's a concern, you may want to look into options that check for existence via queries first, or use an INSERT ON DUPLICATE KEY UPDATE (mysql) type query for inserting.

    0 讨论(0)
提交回复
热议问题