执行此操作的“传统”方法是使用实体-属性-值或 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 功能,即使查询问题也可以克服。
祝你好运!