在rails中为JSON列中的键定义模型上的虚拟属性的正确方法

时间:2016-09-23 05:27:37

标签: ruby ruby-on-rails-4 activerecord metaprogramming

在我的rails模型中,我有一个存储一些元信息的JSON列。 这将从表单输入用户。

由于JSON列的键不是模型的属性,我不能直接在form_for中使用它们,而是需要定义一个虚拟属性。

由于这个虚拟属性的数量可能会增长到任意长度,我想使用元编程来定义属性。

我确实在this question中尝试了答案但是当我在模型中使用常量时,我​​得到一个错误,说明常量是未定义的。所以我直接在数组中添加了键的符号,并在模块中迭代它们。当我这样做时,我得到一个错误,表示堆栈级别太深。

有人可以帮助我吗?

2 个答案:

答案 0 :(得分:2)

我明白了。我将该属性作为JSON列的键返回,现在它可以正常工作。

# lib/virtuals.rb
module Virtuals

  %W(key_1 key_2 key_3).each do |attr|
    define_method(attr) do
      self.my_json_column[attr]
    end

    define_method("#{attr}=") do |val|
      self.my_json_column[attr] = val
    end
  end
end

在我的模型中,我只需要包含上面的模块,它在form_for中工作正常,并且也可以正确更新。

答案 1 :(得分:2)

如果您使用PostgreSQL特定列,例如hstorejson,只需使用store_accessor来生成访问者方法。 请注意,这些列使用字符串键控哈希,并且不允许使用符号进行访问。

class Model < ActiveRecord::Base
  store_accessor :my_json_column, [ :key_1, :key_2, key_3 ]
end

引擎盖下做什么?它定义了write \ read辅助方法:

def store_accessor(store_attribute, *keys)
  keys = keys.flatten

  _store_accessors_module.module_eval do
    keys.each do |key|
      define_method("#{key}=") do |value|
        write_store_attribute(store_attribute, key, value)
      end

      define_method(key) do
        read_store_attribute(store_attribute, key)
      end
    end
  end

#.....

store