Assigning to the foreign key on a belongs_to association

I’ve encountered what seems like an odd omission in the behaviour of
belongs_to associations: if you assign to the foreign key attribute,
it doesn’t update the associated object (or mark a previously loaded
one as stale) until you explicitly save or reload it.

Let’s say we have a Company model, which belongs_to :city :

torchbox = Company.find_by_name(‘Torchbox’)
=> #<Company id: 1, name: “Torchbox”, city_id: 1>

torchbox.city
=> #<City id: 1, name: “Oxford”>

torchbox.city_id = 2
=> 2

torchbox.city
=> #<City id: 1, name: “Oxford”>

(Would have expected City id: 2 to be returned here…)

I’ve come up with a 6-line patch (see below) and accompanying plugin
to fix this, but I get the feeling that for something so elementary to
have gone unfixed for so long, there must be some perceived good
reason why it works the way it does. If so, what’s the reason? And if
not, is this a patch that’s worth pushing through the official
channels?

  • Matt

diff --git a/activerecord/lib/active_record/associations.rb b/
activerecord/lib/active_record/associations.rb
index 0809b27…7135e50 100644
— a/activerecord/lib/active_record/associations.rb
+++ b/activerecord/lib/active_record/associations.rb
@@ -1094,6 +1094,13 @@ module ActiveRecord
instance_variable_set(ivar, new_value.nil? ? nil :
association)
end

  •      if association_proxy_class == BelongsToAssociation
    
  •        define_method("#{reflection.primary_key_name}=") do |
    

target_id|

  •          instance_variable_set(ivar, nil)
    
  •          self["#{reflection.primary_key_name}"] = target_id
    
  •        end
    
  •      end
    
  •      define_method("set_#{reflection.name}_target") do |
    

target| return if target.nil? and association_proxy_class
== BelongsToAssociat association =
association_proxy_class.new(self, reflection)
diff --git a/activerecord/test/cases/associations/
belongs_to_associations_test.rb b/activerecord/test/cases/associations/
belongs_to_associations_test.rb
index b8ec911…c130db2 100644— a/activerecord/test/cases/
associations/belongs_to_associations_test.rb
+++ b/activerecord/test/cases/associations/
belongs_to_associations_test.rb
@@ -45,6 +45,13 @@ class BelongsToAssociationsTest <
ActiveRecord::TestCase
assert_equal apple.id, citibank.firm_id
end

  • def test_foreign_key_assignment
  • signals37 = accounts(:signals37)
  • assert_equal companies(:first_firm), signals37.firm
  • signals37.firm_id = companies(:another_firm).id
  • assert_equal companies(:another_firm), signals37.firm
  • end
  • def test_no_unexpected_aliasing
    first_firm = companies(:first_firm) another_firm =
    companies(:another_firm)

rails-core is the place to ask this sort of thing!

Fred