rails icon indicating copy to clipboard operation
rails copied to clipboard

ActiveRecord: Fix preloading when using a double as foreign_key

Open sdrioux opened this issue 3 years ago • 5 comments

Summary

I have a table called accounts in my Postgres database. One of its columns acts as a foreign key for another table in my database called businesses, however, the foreign key is a double, while the primary key is an integer. When trying to preload businesses for accounts, I get the error below.

The issue is that when assigning preloaded associations for a given record, the foreign key gets converted to a string from a float, ie "6.0", while the primary key integer gets converted as "6". As a result, it can't find an owner for the association.

To fix this, I would like to check for the existence of the key as a float, and convert it to an integer first.

It looks like this has been a long-standing issue, and was actually reported back in 2012: https://github.com/rails/rails/pull/6637, but the PR was abandoned.

undefined method `first' for nil:NilClass

/app/vendor/bundle/ruby/2.7.0/gems/activerecord-6.1.4.1/lib/active_record/associations/preloader/association.rb:121:in `block in records_for'
/app/vendor/bundle/ruby/2.7.0/gems/activerecord-6.1.4.1/lib/active_record/core.rb:546:in `init_with_attributes'
/app/vendor/bundle/ruby/2.7.0/gems/activerecord-6.1.4.1/lib/active_record/persistence.rb:403:in `instantiate_instance_of'
/app/vendor/bundle/ruby/2.7.0/gems/activerecord-6.1.4.1/lib/active_record/querying.rb:66:in `block (2 levels) in find_by_sql'
/app/vendor/bundle/ruby/2.7.0/gems/activerecord-6.1.4.1/lib/active_record/result.rb:62:in `block in each'
/app/vendor/bundle/ruby/2.7.0/gems/activerecord-6.1.4.1/lib/active_record/result.rb:62:in `each'
/app/vendor/bundle/ruby/2.7.0/gems/activerecord-6.1.4.1/lib/active_record/result.rb:62:in `each'
/app/vendor/bundle/ruby/2.7.0/gems/activerecord-6.1.4.1/lib/active_record/querying.rb:66:in `map'
/app/vendor/bundle/ruby/2.7.0/gems/activerecord-6.1.4.1/lib/active_record/querying.rb:66:in `block in find_by_sql'
/app/vendor/bundle/ruby/2.7.0/gems/activesupport-6.1.4.1/lib/active_support/notifications/instrumenter.rb:24:in `instrument'
/app/vendor/bundle/ruby/2.7.0/gems/activerecord-6.1.4.1/lib/active_record/querying.rb:61:in `find_by_sql'
/app/vendor/bundle/ruby/2.7.0/gems/activerecord-6.1.4.1/lib/active_record/relation.rb:843:in `block in exec_queries'
/app/vendor/bundle/ruby/2.7.0/gems/activerecord-6.1.4.1/lib/active_record/relation.rb:861:in `skip_query_cache_if_necessary'
/app/vendor/bundle/ruby/2.7.0/gems/activerecord-6.1.4.1/lib/active_record/relation.rb:828:in `exec_queries'
/app/vendor/bundle/ruby/2.7.0/gems/activerecord-6.1.4.1/lib/active_record/relation.rb:631:in `load'

Other Information

I'd like to create a test for this, but I'm not entirely sure how to go about doing that. If anyone has any guidance I would appreciate it.

Thank you!

sdrioux avatar Oct 29 '21 00:10 sdrioux