Rails 6 中如何将 Form Object 与 ActiveRecord 模型建立关联?
问题根因
Rails 提供的has_many等ActiveRecord关联方法,默认仅支持绑定ApplicationRecord的子类(即对应数据库表的实体模型)。Document::TranslationsForm仅混入了ActiveModel::Model,不属于ActiveRecord体系,无法被ActiveRecord关联识别,因此会抛出relation_delegate_class相关的内部方法错误。
此外当前文件路径不符合Rails自动加载规则:带Document命名空间的类需要放在对应层级的目录下,Document::TranslationsForm对应的正确路径是app/forms/document/translations_form.rb,放在根app/forms目录下会导致Rails找不到对应类,也是配置不生效的原因之一。
解决步骤
- 移除Document模型中错误的
has_many :translations_forms关联配置,手动实现Form对象的获取方法
class Document < ApplicationRecord # 保留原本的真实数据关联 has_many :translations # 单个Form场景(适合提交单条翻译) def translations_form Document::TranslationsForm.new(document: self) end # 批量Form场景(适合每条翻译对应一个Form实例) def translations_forms translations.map { |translation| Document::TranslationsForm.new(document: self, translation: translation) } end end
- 调整TranslationsForm的文件路径与代码逻辑
将translations_form.rb移动到app/forms/document/目录下,修改代码如下:
class Document::TranslationsForm include ActiveModel::Model # 声明表单需要的属性 attr_accessor :document, :translation, :content, :language # 添加表单校验规则 validates :content, :language, presence: true def save return false if invalid? # 直接通过document的真实关联创建持久化数据 document.translations.create(content: content, language: language) end end
- 调整调用方式,不使用ActiveRecord关联语法直接操作Form实例
# 单条翻译提交场景 form = document.translations_form form.assign_attributes(translation_params) form.save # 批量翻译提交场景 document.translations_forms.each_with_index do |form, index| form.assign_attributes(translation_params[index]) form.save end
补充说明
如果需要批量提交多条翻译记录,也可以在单个Form对象内接收数组参数,统一校验后一次性创建,减少重复代码:
class Document::TranslationsForm include ActiveModel::Model attr_accessor :document, :translations_list validates :translations_list, presence: true validate :validate_translations_list def save return false if invalid? document.translations.create!(translations_list) true end private def validate_translations_list translations_list.each do |item| next if item[:content].present? && item[:language].present? errors.add(:translations_list, "包含无效的翻译项") end end end
内容的提问来源于stack exchange,提问作者Abdel P.
相关产品推荐
相关产品推荐

