Reputation: 41
I'm working on building an app to keep track of product designs, and I'm having some trouble with my associations. Basically I have a model (Assembly) which needs to have polymorphic association, but also needs to be able to belong to itself.
To illustrate, I have three models: Product, Assembly, and Part.
My model definitions are currently like this:
product.rb
class Product < ActiveRecord::Base
belongs_to :product_family
has_many :assemblies, as: :assemblable
end
assembly.rb
class Assembly < ActiveRecord::Base
belongs_to :assemblable, polymorphic: true
has_many :parts
has_many :subassemblies, as: :assemblable
end
part.rb
class Part < ActiveRecord::Base
belongs_to :assembly
belongs_to :product_family
end
What I would like to be able to do is, given an assembly called "top_assy":
top_assy.subassemblies.create
However, when I try this, I get the following error:
NameError: uninitialized constant Assembly::Subassembly
I'm clearly doing something wrong here - what am I missing? I have already tried adding 'class_name: "Assembly"' as an argument to the 'has_many :subassemblies' command.
Thanks in advance!
Upvotes: 4
Views: 2259
Reputation: 96
I don't know why this works, but I had the same problem and solve as it:
In Assembly class replace
has_many :subassemblies, as: :assemblable
by
has_many :subassemblies, as: :assemblable, class_name: 'Assembly'
=====================================================================
Edit: explanation of solution
Before specifying :class_name:
When calling .subassemblies method, rails queries a supposed 'Subassembly' model class to match the 'assemblable_id' column in that class. However, 'Subassembly' model class is not defined (it doesn't make sense to define it anyway) here and hence the error.
After specifying :class_name:
Because the class 'Assembly' was specified as :class_name, now rails knows it is to query the 'Assembly' model class and match the 'assemblable_id' column.
Demonstation of flow:
# :class_name has been specified to be 'Assembly'
ex_asm = Assembly.new # an example assembly
ex_asm.subassemblies # flow:
# 1. Rails checks the :subassemblies association
# 2.a. There it is specified to query the class 'Assembly'
# 2.b. and it is to match the "id" column of ex_asm
# 2.c. with the 'assemblable_id' column of the Assembly table
# 3 Rails returns the assemblies matching criteria (2) as
# :subassemblies of ex_asm.
Upvotes: 1
Reputation: 33
has_many :subassemblies, as: :assemblable
by
has_many :subassemblies, as: :assemblable, class_name: 'Assembly'
Carlos's solution works because now rails knows which class to query, as follows:
Before specifying :class_name:
When calling .subassemblies method, rails queries a supposed 'Subassembly' model class to match the 'assemblable_id' column in that class. However, 'Subassembly' model class is not defined (it doesn't make sense to define it anyway) here and hence the error.
After specifying :class_name:
Because the class 'Assembly' was specified as :class_name, now rails knows it is to query the 'Assembly' model class and match the 'assemblable_id' column.
Demonstation of flow:
# :class_name has been specified to be 'Assembly'
ex_asm = Assembly.new # an example assembly
ex_asm.subassemblies # flow:
# 1. Rails checks the :subassemblies association
# 2.a. There it is specified to query the class 'Assembly'
# 2.b. and it is to match the "id" column of ex_asm
# 2.c. with the 'assemblable_id' column of the Assembly table
# 3 Rails returns the assemblies matching criteria (2) as
# :subassemblies of ex_asm.
Upvotes: 1
Reputation: 991
you can try this
product.rb
class Product < ActiveRecord::Base
belongs_to :product_family
has_many :assemblies
end
assembly.rb
class Assembly < ActiveRecord::Base
attr_accessible :top_assembly_id
has_many :sub_assemblies, :class_name => "Assembly", :foreign_key => "top_assembly_id"
belongs_to :top_assembley, :class_name => "Assembly"
has_many :parts
end
part.rb
class Part < ActiveRecord::Base
belongs_to :assembly
belongs_to :product_family
end
and now you can referer
top_assembley.sub_assemblies.create
Upvotes: 0