实现此功能的“传统”方法是使用实体-属性-值(Entity-Attribute-Value,简称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
时,Rails会为你完成这些操作)。
然后在你的模型中:
然后在你的模型中:
class Customer < ActiveRecord::Base
has_many :customer_properties
end
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
customer.customer_properties.create(key: "email", value: "foo@bar.com")
就数据库设计而言,这是相当坚实的,但正如你所见,它有一些限制:特别是,使用起来很麻烦。此外,你只能使用单个值类型 (:string
/VARCHAR
是常见的)。如果你选择这条路,你可能想要在"Customer"上定义一些便利方法,以使访问和更新属性变得更加简便。我猜测可能有一些针对使用 ActiveRecord 与 EAV 模式协同工作的宝石,但我不知道它们是什么,希望你原谅我没有搜索,因为我在移动设备上。
正如 Brad Werth 所指出的,如果你只需要存储任意属性而不查询它们,serialize
是一个很好的替代方案,如果你使用 PostgreSQL,甚至查询问题也可以通过其伟大的 hstore 特性克服。
祝你好运!