Hello,
I’m instantiating the results of a multi-table, find_by_sql query. I
start by creating an association graph as an argument to
JoinDependency. The instantiation fails if the graph contains any
duplicate association names, but it works if the associations are
unique. (The sql query executes correctly even when instantiation
fails)
I can’t think of a reason for an association name to be unique across
different models in a graph, so I’m wondering if this is a bug?
Thanks,
Tom
Here’s an example of the problem:
#Create tables
create table a_foos(id int(11) DEFAULT NULL auto_increment PRIMARY
KEY, name varchar(255));
create table b_foos(id int(11) DEFAULT NULL auto_increment PRIMARY
KEY, name varchar(255), a_foo_id int, c_foo_id int, e_foo_id int);
create table c_foos(id int(11) DEFAULT NULL auto_increment PRIMARY
KEY, name varchar(255),type varchar(255));
create table d_foos(id int(11) DEFAULT NULL auto_increment PRIMARY
KEY, name varchar(255), c_foo_id int, e_foo_id int);
create table e_foos(id int(11) DEFAULT NULL auto_increment PRIMARY
KEY, name varchar(255));
#Define models
class AFoo < ActiveRecord::Base
has_many :b_foos
end
class BFoo < ActiveRecord::Base
belongs_to :a_foo
belongs_to :c_foo
belongs_to :e_foo
end
class CFoo < ActiveRecord::Base
has_many :b_foos
has_many :d_foos
end
class DFoo < ActiveRecord::Base
belongs_to :c_foo
belongs_to :d_foo
end
class EFoo < ActiveRecord::Base
has_many :b_foos
has_many :d_foos
end
#Create partial data
a_foo = AFoo.new(:name=>“some AFoo”)
a_foo.save!
b_foo = BFoo.new(:name=>“some BFoo”, :a_foo=>a_foo)
b_foo.save!
#Setup Join Dependency and create SQL Query
assoc_graph = [{:b_foos=>[{:c_foo=>[:d_foos]}, {:e_foo=>[:d_foos]}]}]
#association, :d_foos, exists on two models in graph
join_dep =
ActiveRecord::Associations::ClassMethods::JoinDependency.new(AFoo,
assoc_graph, nil)
sql_selects = []
sql_joins = []
for i in 0…join_dep.join_associations.size-1
assoc = join_dep.join_associations[i]
sql_selects += assoc.parent.column_names_with_alias.collect{ |map|
“#{assoc.parent.aliased_table_name}.#{map[0]}
AS #{map[1]}” } if
i==0 #base
sql_selects += assoc.column_names_with_alias.collect{ |map|
“#{assoc.aliased_table_name}.#{map[0]}
AS #{map[1]}” }
sql_joins << assoc.association_join
end
query = "
SELECT #{sql_selects.join(",\n “)}
FROM #{join_dep.join_associations[0].parent.table_name}
#{join_dep.join_associations[0].parent.aliased_table_name}
#{sql_joins.join(”\n")}
"
#The query runs correctly
#±------±----------±------±----------±------+…
#| t0_r0 | t0_r1 | t1_r0 | t1_r1 | t1_r2 |…
#±------±----------±------±----------±------+…
#| 1 | some AFoo | 1 | some BFoo | 1 |…
#±------±----------±------±----------±------+…
results = AFoo.find_by_sql(query)
=> [#]
eager_afoos = join_dep.instantiate(results)
NoMethodError: undefined method d_foos' for #<BFoo:0x2a27978> from /tmp/Main/vendor/rails/activerecord/lib/active_record/ attribute_methods.rb:205:in
method_missing’
from /tmp/Main/vendor/rails/activerecord/lib/active_record/
associations.rb:1486:in send' from /tmp/Main/vendor/rails/activerecord/lib/active_record/ associations.rb:1486:in
construct_association’
from /tmp/Main/vendor/rails/activerecord/lib/active_record/
associations.rb:1475:in construct' from /tmp/Main/vendor/rails/activerecord/lib/active_record/ associations.rb:1474:in
each’
from /tmp/Main/vendor/rails/activerecord/lib/active_record/
associations.rb:1474:in construct' from /tmp/Main/vendor/rails/activerecord/lib/active_record/ associations.rb:1471:in
construct’
from /tmp/Main/vendor/rails/activerecord/lib/active_record/
associations.rb:1470:in each' from /tmp/Main/vendor/rails/activerecord/lib/active_record/ associations.rb:1470:in
construct’
from /tmp/Main/vendor/rails/activerecord/lib/active_record/
associations.rb:1476:in construct' from /tmp/Main/vendor/rails/activerecord/lib/active_record/ associations.rb:1474:in
each’
from /tmp/Main/vendor/rails/activerecord/lib/active_record/
associations.rb:1474:in construct' from /tmp/Main/vendor/rails/activerecord/lib/active_record/ associations.rb:1471:in
construct’
from /tmp/Main/vendor/rails/activerecord/lib/active_record/
associations.rb:1470:in each' from /tmp/Main/vendor/rails/activerecord/lib/active_record/ associations.rb:1470:in
construct’
from /tmp/Main/vendor/rails/activerecord/lib/active_record/
associations.rb:1402:in instantiate' from (irb):41:in
each_with_index’
from /tmp/Main/vendor/rails/activerecord/lib/active_record/
associations.rb:1397:in each' from /tmp/Main/vendor/rails/activerecord/lib/active_record/ associations.rb:1397:in
each_with_index’
from /tmp/Main/vendor/rails/activerecord/lib/active_record/
associations.rb:1397:in `instantiate’
from (irb):41
from :0>>
Instantiation works if I use an assoc_graph without multiple :d_foos
associations:
assoc_graph = [{:b_foos=>[{:c_foo=>[:d_foos]}, {:e_foo=>[:d_foos]}]}]
vs
assoc_graph = [{:b_foos=>[:c_foo, {:e_foo=>[:d_foos]}]}]