Rails 4 HABTM custom validation on associations
activerecord, has-and-belongs-to-many, ruby-on-rails, ruby-on-rails-4, validation
Solution
`user.roles << role` performs no validation on `user`. The user is largely uninvolved. All this does is insert a new record into your joining table.
If you want to enforce that a user has only one role, you have two options, both involve throwing away `has_and_belongs_to_many`, which you really shouldn't use anymore. Rails provides `has_many :through`, and that has been the preferred way of doing many-to-many relationship for some time.
So, the first (and I think best) way would be to use `has_many`/`belongs_to`. That is how you model one-to-many relationships in Rails. It should be this simple:
class Role
has_many :users
end
class User
belongs_to :role
end
The second way, which is over complex for enforcing a single associated record, is to create your joining model, call it `UserRole`, use a `has_many :through`, and perform the validation inside `UserRole`.
class User
has_many :user_roles
has_many :roles, through: :user_roles
end
class UserRole
belongs_to :user
belongs_to :role
# Validate that only one role exists for each user
validates :user_id, uniqueness: { scope: :role_id }
# OR, to validate at most X roles are assigned to a user
validate :at_most_3_roles, on: :create
def at_most_3_roles
duplicates = UserRole.where(user_id: user_id, role_id: role_id).where('id != ?', id)
if duplicates.count > 3
errors.add(:base, 'A user may have at most three roles')
end
end
end
class Role
has_many :user_roles
has_many :users, through: :user_roles
end
Problem
I've got a simple scenario, but I can't seem to find any proposed solutions that apply to Rails 4. I want to simply add a custom validator that checks the amount of stored associations between my HABTM association. Easier said and done, to my surprise? I've searched for a solution but only end up with answers for older versions of Rails it seems. I've got the following: ``` class User < ActiveRecord::Base has_and_belongs_to_many :roles after_save :check_maximum_number_of_roles . . . private def check_maximum_number_of_roles if self.roles.length > 3 errors.add(:roles, 'Users can only have three roles assigned.') return false end end end class Role < ActiveRecord::Base has_and_belongs_to_many :users end ``` The reason I use `after_save` is because as far as I understand the stored association is first available after it has been added. I've also tried to write an custom validator (e.g. `validate: :can_only_have_one_role`), but that does not work either. I add the association in the following manner and have done this in the rails console (which should work just fine?): ``` user.roles << role ``` Nevertheless, it adds more than one role to users and does not care of any type of validation. Help much appreciated, thanks!