0

我有一个模型,ModelRun接受另一个模型的嵌套属性,ParameterValue。( ModelRun has_many :parameter_values.) 但是,ParameterValue也使用单表继承来保存两个子类:NumericParameterFileParameter. FileParameter使用CarrierWave存储文件。

问题在于,在ModelRunController保存或更新 aModelRun时,默认情况下@model_run.save@model_run.update_attributes不识别ParameterValue属性的类型 - 它只是尝试将它们存储为ParameterValue. 这适用于NumericParameter值,但它引发了一个例外,FileParameters因为CarrierWave 上传器没有被挂载来处理文件上传,因此 ActiveRecord 在尝试将文件序列化到数据库时失败。

处理这个问题的最干净的方法是什么?我想到的唯一解决方案是在控制器和方法中手动填充@model_run.parameter_values集合,因为我可以分辨每个应该是哪种类型并一个一个地创建正确的对象。然而,这似乎重新实现了很多 Rails 魔法,因为我不能只使用或不再使用 - 似乎它首先放弃了使用的大部分优势。有没有更好的方法,Rails Way™?createupdateParameterValueModelRun.new(params[:model_run])@model_run.update_attributesaccepts_nested_attributes_for

每个模型的相关部分复制如下。

模型运行.rb

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

参数值.rb

class ParameterValue < ActiveRecord::Base
  belongs_to :model_run

  attr_accessible :type,
                  :parameter_id, 
                  :description, 
                  :numeric_value,
                  :model_run_id,
                  :parameter_file
end

数字参数.rb

class NumericParameter < ParameterValue
  attr_accessible :numeric_value
end

文件参数.rb

class FileParameter < ParameterValue
  mount_uploader :parameter_file, ParameterFileUploader

  attr_accessible :parameter_file
end

参数文件上传器.rb

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
4

1 回答 1

4

如果我很了解您,您正试图通过传递:type? 来找到在 STI 层次结构中实例化正确子类的便捷方法。如果您以后不需要更改类型,您可以将此技巧添加到您的 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 instatntiating,包括上传者,验证等。

于 2012-04-06T06:29:45.123 回答