Conceptually what I am trying to do is to be able to say: product a is available in 5 colors, which will be an array of color ids. These color ids are the keys that link to a colors table that holds color related data such as hex representations, color images, and so on. Finally, I want to join the colors table with the products table so that I can pull over color related data.
In my current setup, I have two tables: products
and colors
. In my products
table I have a column ( color_ids
) that holds an array of integers. This array holds the ids of the colors
in the colors
table. To join the two in Rails, I created a custom SQL string that is in the Product
class eg:
class Product < ApplicationRecord
has_many :colors
def self.custom_query
"SELECT * FROM products JOIN colors on colors.id = ANY(products.color_ids)
WHERE products.name = 'Some Product'"
end
end
I tried using associations ( includes(:colors)
) but that doesn't seem to work since the primary id is an array of ids.
Is there a more elegant / Rails way to achieve what I am trying to do?
Use simple has_and_belongs_to_many association. Don't store your reference ids in an array, just because PostgreSQL kind of allows you to do so, it's not how relations should be implemented in relational databases.
# new migration
create_table :colors_products do |t|
t.references :color, foreign_key: true
t.references :product, foreign_key: true
end
add_index :colors_products, [:color_id, :product_id], unique: true
class Product < ApplicationRecord
has_and_belongs_to_many :colors
end
class Color < ApplicationRecord
has_and_belongs_to_many :products
end
All of the ActiveRecord methods will work then.
Why shouldn't you have relations made with arrays (unless you really know what you're doing):
You can simply do
class Product < ApplicationRecord
def colors
Color.where(id: color_ids)
end
end
class Color < ApplicationRecord
def products
Product.where('? = Any (category_ids)', id)
end
end
You could create a view named like:
CREATE VIEW product_colors(id, product_id, name, hex, image) AS
SELECT
p.id as product_id,
c.name,
c.hex,
c.image
FROM
(
SELECT
id,
unnest(color_ids) as color_id
FROM
products
) as p
INNER JOIN colors c
ON c.id = p.color_id;
And then add model ProductColor
with
def readonly?
true
end
And you can treat this view as a child model. Products.includes(:product_colors)
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.