0

我有一个媒体模型,它有一堆标准的元数据属性,并且像往常一样保存在数据库中。我现在要做的是在现有属性之上为这个模型添加一些可配置的元数据属性。这些属性的列表将在配置文件中定义并在运行时加载。它们将作为一系列与主模型关联的属性值对存储在数据库中的不同表中。

所以,我目前的代码是,

class Media < ActiveRecord::Base
  has_many :custom_metadata

  attr_accessible :title, :language, :copyright, :description
end

class CustomMetadata < ActiveRecord::Base
  belongs_to :media

  attr_accessible :name, :value
end

我想要做的是能够以与标准元数据属性相同的方式访问和更新媒体模型上的自定义元数据属性。例如,如果自定义元数据属性被称为发布者和贡献者,那么我想在媒体模型中访问它们,@media.publisher即使@media.contributor它们将@media.custom_metadata在其值类似于的关联中[{:name => 'publisher', :value => 'Fred'}, {:name => 'contributor', :value => 'Bill'}]

似乎虚拟属性是实现这一目标的最佳方式,但我能找到的所有使用虚拟属性的人的例子都是属性的名称是静态的并且是已知的,而不是运行时配置中的动态,所以他们可以定义方法,例如,publisher然后publisher=将包含写入相关关联的属性值记录的代码。

attr_accessor *Settings.custom_metadata_fields我可以使用(假设Settings.custom_metadata_fields返回)在类上定义属性[:publisher, :contributor],也可以使用类似的技术使用attr_accessible.

我坚持的部分是如何在从记录中加载数据时从关联中填充虚拟属性,然后反过来,如何在保存记录之前将虚拟属性中的数据传递回关联中。

我目前看到这项工作的两种方式是使用method_missingor attribute_missing,或者可能是 viainitializebefore_save回调?无论哪种情况,鉴于我的模型混合了普通属性和虚拟属性,我不确定如何定义它。

有什么建议么?

4

2 回答 2

1

使用回调听起来很合理。你用的是什么数据库?如果是 PostgreSQL,也许你应该看看 HStore 扩展(http://www.postgresql.org/docs/9.2/static/hstore.html)它会表现得更好,并且有一些宝石使它易于使用。

于 2013-05-23T11:31:44.213 回答
0

在进一步研究了回调之后,我发现了after_initialize回调,这比使用initialize我最初计划的方法要好得多。

最后,这是 Media 模型的最终代码,我没有更改我在问题中定义的 CustomMetadata 模型中的任何内容,

class Media < ActiveRecord::Base
  has_many :custom_metadata

  attr_accessor *Settings.custom_metadata_fields

  attr_accessible :title, :language, :copyright, :description
  attr_accessible *Settings.custom_metadata_fields

  validates_presence_of *Settings.required_custom_fields

  before_save :save_custom_metadata
  after_initialize :load_custom_metadata

  def load_custom_metadata
    MediaMetadata.custom_all_fields.each do |field|
      custom_record = custom_metadata.where(:name => field.to_s).first_or_initialize()
      send("#{field}=", custom_record.value)
    end
  end

  def save_custom_metadata
    MediaMetadata.custom_all_fields.each do |field|
      custom_record = custom_metadata.where(:name => field.to_s).first_or_initialize()
      custom_record.value = send(field)
      if custom_record.value.blank?
        custom_record.destroy
      else
        custom_record.save
      end
    end
  end
end

这个解决方案有几个很好的好处。首先,它不会影响 Media 模型上的任何正常属性。其次,只有具有实际值的自定义元数据才会存储在自定义元数据表中。如果该值为空,则该记录将被完全删除。最后,我可以对模型属性使用标准验证,如我所需的自定义元数据属性所示。

于 2013-05-24T00:16:54.470 回答