我想改变has_many关联行为
考虑这个基本数据模型
class Skill < ActiveRecord::Base
has_many :users, through: :skills_users
has_many :skills_users
end
class User < ActiveRecord::Base
has_many :skills, through: :skills_users, validate: true
has_many :skills_users
end
class SkillsUser < ActiveRecord::Base
belongs_to :user
belongs_to :skill
validates :user, :skill, presence: true
end
为了增加新技能,我们可以轻松地做到这一点:
john = User.create(name: 'John Doe')
tidy = Skill.create(name: 'Tidy')
john.skills << tidy
但如果你这样做了两次,我们就会获得此用户的重复技能
防止这种情况的可能性是在添加
之前进行检查john.skills << tidy unless john.skills.include?(tidy)
但这很有意思......
我们也可以像{/ p>一样改变ActiveRecord::Associations::CollectionProxy#<<行为
module InvalidModelIgnoredSilently
def <<(*records)
super(records.to_a.keep_if { |r| !!include?(r) })
end
end
ActiveRecord::Associations::CollectionProxy.send :prepend, InvalidModelIgnoredSilently
强制CollectionProxy
忽略透明地添加重复记录。
但我对此并不满意。
我们可以在SkillsUser
class SkillsUser < ActiveRecord::Base
belongs_to :user
belongs_to :skill
validates :user, :skill, presence: true
validates :user, uniqueness: { scope: :skill }
end
但在这种情况下,添加两次会提升ActiveRecord::RecordInvalid
,我们必须在添加
或在CollectionProxy
module InvalidModelIgnoredSilently
def <<(*records)
super(valid_records(records))
end
private
def valid_records(records)
records.with_object([]).each do |record, _valid_records|
begin
proxy_association.dup.concat(record)
_valid_records << record
rescue ActiveRecord::RecordInvalid
end
end
end
end
ActiveRecord::Associations::CollectionProxy.send :prepend, InvalidModelIgnoredSilently
但我对此仍然不满意。
对我来说,CollectionProxy上的理想和可能缺少的方法是:
john.skills.push(tidy)
=> false
和
john.skills.push!(tidy)
=> ActiveRecord::RecordInvalid
知道我怎么能做得很好吗?
- 编辑 -
我发现避免抛出异常的方法就是抛出异常!
class User < ActiveRecord::Base
has_many :skills, through: :skills_users, before_add: :check_presence
has_many :skills_users
private
def check_presence(skill)
raise ActiveRecord::Rollback if skills.include?(skill)
end
end
不是基于验证,既不是通用解决方案,也可以帮助......
答案 0 :(得分:0)
也许我不理解这个问题,但这就是我要做的事情:
答案 1 :(得分:0)
您能告诉我创建SkillsUser表的迁移吗? 如果你向我展示你拥有的SkillsUser表的索引,那就更好了。 我通常使用has_and_belongs_to_many而不是has_many - through。 尝试添加此迁移
$ rails g migration add_id_to_skills_users id:primary_key
# change the has_many - through TO has_and_belongs_to_many
如果您有双重索引“skills_users”,则无需验证。
希望它可以帮助你。