Lighthouse has a new layout. Prefer the old one? Return to the old layout, and switch back any time from the link at the top of each page.

This project is archived and is in readonly mode.

belongs to association eager/pre- load fails when foreign key is same as association name

#2187

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

Reported by Minh Tran · March 9th, 2009 @ 11:22 PM

State: invalid
Milestone: 2.x
Assigned to: Ryan Bigg Ryan Bigg
Importance: none

Activity

  1. David Trasbo
    David Trasbo
    • Assigned user set to Ryan Bigg

    Giving a column and an association the same name is bound to cause trouble. Rails doesn't and shouldn't try to support doing things like this. Rename the column created_by_id, e.g. This ticket can be marked as invalid.

    April 15th, 2010 @ 08:04 PM

  2. David Trasbo
    David Trasbo

    Giving a column and an association the same name is bound to cause trouble. Rails doesn't and shouldn't try to support doing things like this. Rename the column created_by_id, e.g. This ticket can be marked as invalid.

    April 15th, 2010 @ 08:04 PM

  3. Ryan Bigg
    Ryan Bigg
    • State changed from new to invalid

    Yup, never give your association the name of the foreign key, always give the foreign_key an _id suffix.

    April 15th, 2010 @ 10:26 PM