我有一个模型ModelRun
,它接受另一个模型ParameterValue
的嵌套属性。 (ModelRun has_many :parameter_values
。)但是,ParameterValue
还使用单表继承来保存两个子类:NumericParameter
和FileParameter
。 FileParameter
使用CarrierWave来存储文件。
问题在于ModelRunController
保存或更新ModelRun
时,默认情况下,@model_run.save
或@model_run.update_attributes
无法识别ParameterValue
属性的类型 - 它只是试图将它们存储为ParameterValue
。这适用于NumericParameter
值,但它会引发FileParameters
的异常,因为CarrierWave uploader doesn't get mounted处理文件上载,因此在尝试将文件序列化到数据库时ActiveRecord失败。
处理此问题的最简洁方法是什么?我遇到的唯一解决方案是在控制器的@model_run.parameter_values
和create
方法中手动填充update
集合,因为我可以告诉每个ParameterValue
应该是哪种类型并创建正确的对象一个接一个。然而,这似乎重新实现了很多Rails魔法,因为我不能再使用ModelRun.new(params[:model_run])
或@model_run.update_attributes
- 似乎它抛弃了在第一个中使用accepts_nested_attributes_for
的大部分优势地点。有没有更好的方法,Rails Way™?
每个模型的相关部分都复制在下面。
class ModelRun < ActiveRecord::Base
has_many :parameter_values, dependent: :destroy
accepts_nested_attributes_for :parameter_values, allow_destroy: true
attr_accessible :name,
:description,
:geometry_description,
:run_date,
:run_date_as_string,
:parameter_values_attributes
end
class ParameterValue < ActiveRecord::Base
belongs_to :model_run
attr_accessible :type,
:parameter_id,
:description,
:numeric_value,
:model_run_id,
:parameter_file
end
class NumericParameter < ParameterValue
attr_accessible :numeric_value
end
class FileParameter < ParameterValue
mount_uploader :parameter_file, ParameterFileUploader
attr_accessible :parameter_file
end
class ParameterFileUploader < CarrierWave::Uploader::Base
storage :file
def store_dir
"#{Rails.root}/uploads/#{model.class.to_s.underscore}/#{model.id}"
end
def cache_dir
"#{Rails.root}/tmp/uploads/cache/#{model.id}"
end
end
答案 0 :(得分:4)
如果我理解你,你试图通过传递:type?来找到在STI层次结构中实例化正确子类的方便方法。如果以后不需要更改类型,可以将此hack添加到ParameterValue类
class ParameterValue < ActiveRecord::Base
class << self
def new_with_cast(*attributes, &block)
if (h = attributes.first).is_a?(Hash) && !h.nil? && (type = h[:type] || h['type']) && type.length > 0 && (klass = type.constantize) != self
raise "wtF hax!!" unless klass <= self
return klass.new(*attributes, &block)
end
new_without_cast(*attributes, &block)
end
alias_method_chain :new, :cast
end
end
在此之后,传递正确的类型将导致正确的ParameterValue实例化,包括上传,验证等。