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

Correct way to define virtual attributes on a Model for the keys in a JSON column in rails

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

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

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

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

有人可以帮我吗?

我明白了。我 return 属性作为 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 中工作正常并且也可以正确更新。

如果您正在使用 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