Неограниченное количество произвольных свойств (пар ключ/значение) для модели ActiveRecord.

Используя ruby ​​on rails, у меня есть таблица Customer, в которую я хочу добавить неограниченное количество свойств (пар ключ-значение). Я еще не уверен, какими будут пары ключ/значение, поэтому не знаю, как это сделать. Например, одним клиентом может быть:

  • Customer 1 properties:
    • color: 'yellow'
    • бренд: «найк»
    • продажи: «33»
  • Customer 2 properties:
    • color: 'red'
    • phone_number: '1111111111'
    • покупки: '2'

По сути, клиенты могут иметь любое количество свойств в паре ключ/значение.

Как я могу это сделать?


person Matthew Berman    schedule 15.04.2014    source источник


Ответы (3)


Вы должны иметь возможность использовать для этого serialize и назначить хэш свойств к вашему атрибуту свойств и извлекайте их таким же образом.

person Brad Werth    schedule 15.04.2014
comment
Это, однако, затруднит, если не сделает невозможным, запрос по этим свойствам. - person Jordan Running; 17.04.2014
comment
Это зависит от используемой базы данных. Postgres легко поддерживает это — travisjeffery.com/b/2012/ 02/использование-postgress-hstore-with-rails - person Brad Werth; 17.04.2014
comment
Да, если вы установите gem. - person Jordan Running; 17.04.2014
comment
Это сложно, если не невозможно? Кроме того, Rails 4 изначально поддерживает его. Почему вы просто не указали, что для этого может потребоваться драгоценный камень? - person Brad Werth; 17.04.2014
comment
Потому что я не знал о геме, и не все используют Postgres? Ваш ответ был хорошим, с оговорками - я просто хотел убедиться, что ОП знает одно из этих предостережений. - person Jordan Running; 17.04.2014
comment
Прямо сейчас, их, вероятно, стоит упомянуть в фактическом ответе по причинам, которые вы упомянули... - person Brad Werth; 17.04.2014

«Традиционный» способ сделать это — использовать шаблон 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          |
+----+-------------+--------------+------------+

Вам определенно понадобится INDEX для key и возможно для value (и, конечно, customer_id, но Rails сделает это за вас, когда вы будете использовать 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: "[email protected]")
# => #<CustomerProperty id: 7, customer_id: 1, key: "email", ...>

С точки зрения дизайна базы данных это довольно надежно, но, как вы можете видеть, у него есть некоторые ограничения: в частности, это громоздко. Кроме того, вы ограничены одним типом значения (обычно :string/VARCHAR). Если вы пойдете по этому пути, вы, вероятно, захотите определить некоторые удобные методы для клиента, чтобы сделать доступ и обновление свойств менее громоздкими. Я предполагаю, что, вероятно, есть жемчужины, специально предназначенные для того, чтобы шаблон EAV хорошо работал с ActiveRecord, но я не знаю их сразу, и я надеюсь, что вы простите меня за то, что я не гуглил, так как я мобилен.

Как указывает Брэд Верт, если вам нужно просто хранить произвольные свойства, а не запрашивать их, serialize — отличная альтернатива, а если вы используете PostgreSQL, то даже проблема с запросами преодолима благодаря замечательной функции hstore.

Удачи!

person Jordan Running    schedule 16.04.2014
comment
Кстати, вы избавили меня от проблем - хорошо сказано! - person Brad Werth; 17.04.2014
comment
Вау, отличный ответ (и ты тоже, Брэд). Я использовал сериализацию, но это тоже очень интересно. Спасибо - person Matthew Berman; 18.04.2014
comment
Кто-нибудь знает какие-либо стандартные драгоценные камни Rails для использования этого шаблона в нескольких моделях Rails? - person Eric Walker; 09.09.2017

Вы можете изучить гем hydra_attribute, который является реализацией Entity-Attribute-Value ( EAV) для моделей ActiveRecord.

person Javid Jamae    schedule 12.02.2015