Rails中has_many :through关联验证问题:如何正确创建关联?
解决Rails has_many :through关联的存在性验证问题
问题根源
你遇到的问题是c.users.create!的执行顺序导致的:Rails会先尝试保存User对象,此时关联的CompanyUser还未被创建,数据库中没有对应的关联记录,而你原有的validates :company_users, presence: true验证只会检查数据库中的记录,因此验证失败。
解决方案
方案一:自定义验证(推荐)
修改User模型,用自定义验证同时检查内存中已构建的关联和数据库中的关联:
class User < ApplicationRecord has_many :company_users, dependent: :destroy has_many :companies, through: :company_users validate :must_have_at_least_one_company private def must_have_at_least_one_company # 同时检查内存中的company_users和companies关联 unless companies.any? || company_users.any? errors.add(:companies, "必须至少关联一个公司") end end end
当你调用c.users.create!时,Rails会在内存中自动构建对应的CompanyUser关联,company_users.any?会返回true,验证通过后,Rails会依次保存User和CompanyUser到数据库。
方案二:配置inverse_of并验证companies关联
调整三个模型的关联定义,添加inverse_of确保内存中的双向关联被识别,同时把验证目标改为companies:
class User < ApplicationRecord has_many :company_users, dependent: :destroy, inverse_of: :user has_many :companies, through: :company_users, inverse_of: :users validates :companies, presence: true end class Company < ApplicationRecord has_many :company_users, dependent: :destroy, inverse_of: :company has_many :users, through: :company_users, inverse_of: :companies end class CompanyUser < ApplicationRecord belongs_to :user, inverse_of: :company_users belongs_to :company, inverse_of: :company_users end
inverse_of会让Rails在内存中建立关联对象的双向引用,当通过c.users.create!创建用户时,内存中user.companies已经包含了当前的公司c,此时validates :companies, presence: true会识别到这个内存中的关联,从而通过验证。
方案三:手动构建关联后保存
如果不想修改模型,也可以手动在内存中构建关联再保存:
c = Company.create user = User.new(email: "test@example.com") user.company_users.build(company: c) user.save!
这种方式会提前在内存中创建CompanyUser关联,执行user.save!时,company_users的存在性验证会通过。
内容的提问来源于stack exchange,提问作者NGobin
相关产品推荐
相关产品推荐

