I have a Product and Category model structure, however, the attributes that belong to each product are dependent upon which category they are in.
Please note that this is not about STI (single table inheritance) as I am not implementing specific classes that inherit from Product
(eg like Book < Product
, Bicycle < Product
etc)
I have the following model classes (simplified to the pertinent code only):
class Product < ApplicationRecord
# Relationships
belongs_to :subcategory
delegate :category, to: :subcategory, allow_nil: true
has_many :product_category_attributes
end
class Category < ApplicationRecord
# Relationships
has_many :subcategories, dependent: :destroy
has_many :products, through: :subcategories
has_many :category_attributes, dependent: :destroy
end
class CategoryAttribute < ApplicationRecord
# Relationships
belongs_to :category
has_many :product_category_attributes, dependent: :destroy
end
class ProductCategoryAttribute < ApplicationRecord
# Relationships
belongs_to :product
belongs_to :category_attribute
end
This works but...bear with me here!
Here is the migrations for the category hierarchy:
class CreateCategories < ActiveRecord::Migration[5.1]
def change
create_table :categories, id: :uuid do |t|
t.string :name
t.timestamps
end
end
end
class CreateCategoryAttributes < ActiveRecord::Migration[5.1]
def change
create_table :category_attributes, id: :uuid do |t|
t.references :category, type: :uuid, foreign_key: true, index: true
t.string :name
t.string :value
t.timestamps
end
end
end
class CreateProductCategoryAttributes < ActiveRecord::Migration[5.1]
def change
create_table :product_category_attributes, id: :uuid do |t|
t.references :product, type: :uuid, foreign_key: true
t.references :category_attribute, type: :uuid, foreign_key: true
t.timestamps
end
add_index :product_category_attributes, [:product_id, :category_attribute_id], unique: true, name: 'index_product_category_attributes_on_fks'
end
end
Imagine a category with the name 'Rugs'. Rugs have a size, so:
category_attribute.name
would be 'size'
and
category_attribute.values
would have 3 - 'small', 'medium', 'large'
So in the `category_attribute' table:
| id | name | value
| 1 | size | small
| 2 | size | medium
| 3 | size | large
But an individual Rug product might only be available in 'small'.
So that's the reason for the ProductCategoryAttribute
-> I can assign only the attributes that applies to that particular product.
What I don't understand is how can I 'short-cut' the ActiveRecord associations created.
For example, if I load a Product
in memory, how can I modify the associations so that I can do:
product.category_attributes
or even product.attributes
to return only those applicable to that product.
As an example, I know I can get a single attribute like this:
p.product_category_attributes.first.category_attribute
and that obviously only returns the first one (and is also quite unwieldy).
I believe there is a way to add a method or a scope - I'd like to call it 'attributes' - that could solve my problem, but I am a bit lost on how to proceed.
It seems that attributes
is already reserved by ActiveModel?
Attempt 1 Barking up the wrong tree here but worth a try, eh?
Product.rb
def cat_attributes
self.product_category_attributes.each do |pca|
return pca.category_attribute
end
end
Returns the product_category_attributes
, not the category_attributes
Attempt 2
Product.rb
has_many :category_attributes, through: :product_category_attributes
This returns the data I want, but now how do I alias the name to something shorter?
Attempt 3
Product.rb
has_many :cat_atts, class_name: 'CategoryAttribute', through: :product_category_attributes, source: :category_attributes
Attempt 4
Delete *.*
ReturnTo C#
Thanks.
according to Rails Guide you can go with your attempt 3 version. Like:
# app/models/product.rb
has_many :product_category_attributes
has_many :cat_atts, through: :product_category_attributes, source: :category_attribute
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.