ActiveRecord模型的无限任意属性(键/值对)

Mat*_*man 8 activerecord ruby-on-rails entity-attribute-value

在rails上使用ruby,我有一个Customer表,我希望能够添加无限属性(键值对).我不确定键/值对将是什么,所以我不知道如何做到这一点.例如,一个客户可能是:

  • 客户1属性:
    • 颜色:'黄色'
    • 品牌:'nike'
    • 销售额:'33'
  • 客户2属性:
    • 红色'
    • phone_number:'1111111111'
    • 购买:'2'

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

我怎样才能做到这一点?

Jor*_*ing 11

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

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          |
+----+-------------+--------------+------------+
Run Code Online (Sandbox Code Playgroud)

你肯定想要一个INDEX key,也许就是value(customer_id当然,但Rails会在你使用relationbelongs_to迁移时为你做到这一点).

然后在你的模型中:

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

# customer_property.rb
class CustomerProperty < ActiveRecord::Base
  belongs_to :customer
end
Run Code Online (Sandbox Code Playgroud)

这样可以使用如下:

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", ...>
Run Code Online (Sandbox Code Playgroud)

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

正如Brad Werth指出的那样,如果你只需要存储任意属性而不是通过它们进行查询,那么它serialize是一个很好的选择,如果你使用PostgreSQL,即使查询问题也可以克服,因为它有很棒的hstore功能.

祝好运!