ruby-on-rails ActiveRecord验证是否包含在列表中-创建新的关联模型后列表未更新

x6yk4ghg  于 2023-03-31  发布在  Ruby
关注(0)|答案(2)|浏览(120)

我有一个Company模型和一个Employer模型。Employerbelongs_to :companyCompanyhas_many :employers。在我的Employer模型中,我有以下验证:

validates :company_id, inclusion: {in: Company.pluck(:id).prepend(nil)}

我遇到了一个问题,上面的验证失败。下面是一个控制器操作中的示例设置,它将导致验证失败:

company = Company.new(company_params)
# company_params contains nested attributes for employers

company.employers.each do |employer|
  employer.password = SecureRandom.hex
end

company.employers.first.role = 'Admin' if client.employers.count == 1

company.save!
admin = company.employers.where(role: 'Admin').order(created_at: :asc).last

admin.update(some_attr: 'some_val')

在示例代码片段的最后一行,admin.update将失败,因为验证正在检查company_id是否包含在列表中,实际上并不包含,因为列表是在company保存之前生成的。
显然,有一些方法可以解决这个问题,比如获取company.id的值,然后用它来定义admin,但这似乎是一个迂回的解决方案。我想知道的是,是否有更好的方法来解决这个问题。

更新

显然我建议的可能的变通方法根本不起作用。

new_company = Company.find(company.id)
admin = new_company.employers.where(role: 'Admin').order(created_at: :asc).last
admin.update
# Fails validation as before
pu3pd22g

pu3pd22g1#

我不确定我是否完全理解了你的问题,但这部分代码中有一个问题:

validates :company_id, inclusion: {in: Company.pluck(:id).prepend(nil)}

验证是在类级别上配置的,因此它不能很好地与该模型上的更新一起工作(在后续的验证中不会重新评估)。
文档中指出,您可以使用块来包含,所以您也可以尝试这样做:

validates :company_id, inclusion: {in: proc { Company.pluck(:id).prepend(nil) }}

有些人会建议您甚至不进行这种验证,而是对该列进行数据库约束。

ua4mk5z4

ua4mk5z42#

我相信你在这里误用了包含验证器。如果你想验证一个关联的模型存在,而不是它的id列有一个值,你可以用两种方法来做到这一点。在ActivRecord中,你可以使用一个存在验证器。

validates :company, presence: true

您还应该在数据库级别上使用foreign key constraint。这可以防止在关联表中没有相应记录时保存模型。
add_foreign_key:雇主,:公司
如果它通过ActiveRecord,并且没有具有给定company_id的公司记录,则数据库将抛出错误。

相关问题