There is a subtle bug in the association preload code that
causes eager loading not to work when the foreign key name is the
same as the association name. For example,
belongs_to :created_by, :class => 'Person', :foreign_key => 'created_by'
Attached is a diff with a test case that will reveal the
problem, as well as a two-line fix for it. The problem exists in
2.2.2 and AFAIK, 2.3 as well.
To elaborate, the problem occurs in "association_preload.rb" in
method preload_belongs_to_association(). The id/key is obtained by
doing:
record.send(primary_key_name)
However, in the case where the association name is the same as
the foreign key, this will return the associated object,
not the numeric id. Accessing the key/id with [ ] (square
brackets) instead of send() will fix the problem.
This issue does not seem to exist with the other associations
because they use construct_id_map(), which accesses the numeric id
with [] already.
In the attached patch, the test case I wrote is quite simple. If
eager loading worked (i.e. find() with :include), then the number
of generated queries should be small.
For your reference, the failure output for the test case without
the two-line fix would be:
1) Failure:
test_preload_belongs_to_with_foreign_key_name_same_as_association_name(EagerAssociationTest)
[./test/cases/../../lib/active_record/test_case.rb:31:in `assert_queries'
./test/cases/associations/eager_test.rb:785:in `test_preload_belongs_to_with_foreign_key_name_same_as_association_name'
./test/cases/../../../activesupport/lib/active_support/testing/setup_and_teardown.rb:57:in `__send__'
./test/cases/../../../activesupport/lib/active_support/testing/setup_and_teardown.rb:57:in `run']:
4 instead of 2 queries were executed.
Queries:
SELECT * FROM `funny_jokes` WHERE (created_by IS NOT NULL)
SELECT * FROM `people` WHERE (`people`.`id` = 2)
SELECT * FROM `people` WHERE (`people`.`id` = 1)
SELECT * FROM `people` WHERE (`people`.`id` IN (0,0)) .
<2> expected but was
<4>.
2017 tests, 6552 assertions, 1 failures, 0 errors