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
i think the proper validation rule is in your users_roles join model:
validates_uniqueness_of :user_id, :scope => [:role_id]
Perhaps it is possible to create the validation rule
validates_uniqueness_of :user_roles
then catch the validation exception and carry on gracefully. However, this feels really hacky and is very inelegant, if even possible.
As long as the appended role is an ActiveRecord object, what you are doing:
user.roles << role
Should de-duplicate automatically for :has_many
associations.
For has_many :through
, try:
class User
has_many :roles, :through => :user_roles do
def <<(new_item)
super( Array(new_item) - proxy_association.owner.roles )
end
end
end
if super doesn't work, you may need to set up an alias_method_chain.
You can use a combination of validates_uniqueness_of and overriding << in the main model, though this will also catch any other validation errors in the join model.
validates_uniqueness_of :user_id, :scope => [:role_id]
class User
has_many :roles, :through => :user_roles do
def <<(*items)
super(items) rescue ActiveRecord::RecordInvalid
end
end
end
I think you want to do something like:
user.roles.find_or_create_by(role_id: role.id) # saves association to database
user.roles.find_or_initialize_by(role_id: role.id) # builds association to be saved later
This will create only one association in the database even if called multiple times Refer rails guide.
user.roles=[Role.first]