То, что у вас есть, является действительно странным обратным вариантом шаблона значения атрибута сущности (EAV). Было бы более разумно, если бы у вас были нормализованные определения атрибутов (например, объем, вес, количество рисунков и т. Д. c) в одной таблице и полномочия, атрибут и значение в одной таблице.
class Product < ApplicationRecord
has_many :product_attributes
has_many :product_attribute_types, through: :product_attributes
# eager loading scope
def self.eager_load_attributes
eager_load(product_attributes: :product_attribute_types)
end
end
# This is the normalization table that stores the definition of an attribute
# rails g model ProductAttribute name:string unit:string
class ProductAttributeType< ApplicationRecord
has_many :product_attributes
has_many :product_attribute_types, through: :product_attributes
end
# This is the actual table that defines the attributes
# rails g model ProductAttribute product:belongs_to product_attribute_type:belongs_to value:jsonb
class ProductAttribute < ApplicationRecord
belongs_to :product # the entity
belongs_to :product_attribute_type # the attribute
# just shortcuts
delegates :name, to: :product_attribute_type
delegates :unit, to: :product_attribute_type
end
При этом в качестве значения используется столбец JSON, чтобы устранить одну из классических проблем с EAV, а именно то, что вам нужно привести все в один (обычно строковый) тип столбца. JSON может хранить числа (не очень хорошо), строки, массивы и объекты.
Это позволяет вам перебирать товары и атрибуты с помощью:
# eager loading avoids a n+1 query
@products = Product.eager_load_attributes.all
@products.each do |product|
product.product_attributes.each do |attr|
puts "#{attr.name}: #{attr.value}{attr.unit}"
end
end