【问题标题】:Unlimited arbitrary properties (key/value pairs) for an ActiveRecord modelActiveRecord 模型的无限任意属性(键/值对)
【发布时间】:2014-04-15 22:37:24
【问题描述】:

使用 ruby​​ on rails,我有一个 Customer 表,我希望能够向其中添加无限的属性(键值对)。我不确定键/值对是什么,所以我不知道该怎么做。例如,一位客户可能是:

  • 客户 1 属性:
    • 颜色:'黄色'
    • 品牌:“耐克”
    • 销售额:'33'
  • 客户 2 属性:
    • 颜色:“红色”
    • 电话号码:'1111111111'
    • 购买次数:'2'

基本上,客户可以在键/值对中拥有任意数量的属性。

我该怎么做?

【问题讨论】:

    标签: ruby-on-rails activerecord entity-attribute-value


    【解决方案1】:

    执行此操作的“传统”方法是使用实​​体-属性-值或 EAV 模式。顾名思义,您将创建一个包含三列的新表:一列用于“实体”,在本例中为客户,一列用于“属性”名称或键,一列用于值。所以你会有一个这样的表:

    customer_properties
    +----+-------------+--------------+------------+
    | id | customer_id | key          | value      |
    +----+-------------+--------------+------------+
    |  1 |           1 | color        | yellow     |
    |  2 |           1 | brand        | nike       |
    |  3 |           1 | sales        | 33         |
    |  4 |           2 | color        | red        |
    |  5 |           2 | phone_number | 1111111111 |
    |  6 |           2 | purchases    | 2          |
    +----+-------------+--------------+------------+
    

    你肯定会在key 和 也许 在value(当然还有customer_id 上需要一个索引,但是当你使用relation 或belongs_to 在您的迁移中)。

    然后在你的模型中:

    # customer.rb
    class Customer < ActiveRecord::Base
      has_many :customer_properties
    end
    
    # customer_property.rb
    class CustomerProperty < ActiveRecord::Base
      belongs_to :customer
    end
    

    这可以像这样使用:

    customer = Customer.joins(:customer_properties)
                 .includes(:customer_properties)
                 .where(customer_properties: { key: "brand", value: "nike" })
                 .first
    
    customer.customer_properties.each_with_object({}) do |prop, hsh|
      hsh[prop.key] = prop.val
    end
    # => { "color" => "yellow",
    #      "brand" => "nike",
    #      "sales" => "33" }
    
    customer.customer_properties.create(key: "email", value: "foo@bar.com")
    # => #<CustomerProperty id: 7, customer_id: 1, key: "email", ...>
    

    随着数据库设计的发展,这是相当可靠的,但正如您所见,它有一些局限性:特别是,它很麻烦。此外,您仅限于单一值类型(:string/VARCHAR 很常见)。如果你走这条路,你可能希望在 Customer 上定义一些方便的方法,以减少访问和更新属性的麻烦。我猜可能有专门用于使 EAV 模式与 ActiveRecord 很好地配合使用的宝石,但我不知道它们是否在我的脑海中,我希望你能原谅我没有使用谷歌搜索,因为我是移动的。

    正如 Brad Werth 指出的那样,如果您只需要存储任意属性而不是通过它们进行查询,serialize 是一个很好的选择,如果您使用 PostgreSQL,由于其强大的 hstore 功能,即使查询问题也可以克服。

    祝你好运!

    【讨论】:

    • 好吧,你帮我省了麻烦——说得好!
    • 哇,很好的答案(你也是,布拉德)。我使用了序列化,但这也很有趣。非常感谢
    • 有没有人知道任何标准的 Rails gems 可以在多个 Rails 模型中使用这种模式?
    【解决方案2】:

    您可能需要查看 hydra_attribute gem,它是 ActiveRecord 模型的实体-属性-值 (EAV) 模式的实现。

    【讨论】:

      【解决方案3】:

      您应该可以为此使用serialize,并将您的属性哈希分配给您的属性,并以相同的方式检索它们。

      【讨论】:

      • 然而,如果不是不可能的话,这将使通过这些属性进行查询变得困难。
      • 这取决于使用的数据库。 Postgres 轻松支持它 - travisjeffery.com/b/2012/02/using-postgress-hstore-with-rails
      • 是的,如果你安装了 gem。
      • 那是“很难,如果不是不可能的话”?此外,Rails 4 本身就支持它。您为什么不直接指出它可能需要 gem?
      • 因为我不知道 gem,而且不是每个人都使用 Postgres?你的回答很好,有一些警告——我只是想确保 OP 知道其中一个警告。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-07-11
      • 1970-01-01
      • 2016-07-05
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多