开发者

Rails3: Unable to update attribute without first reloading record when creating parent dynamically

开发者 https://www.devze.com 2023-02-21 19:26 出处:网络
Summary Where City has many Locations (HABTM association), create a child record like this: a = Location.create(:name=>\'Site 1\', :city => City.create(:name=>\'A City\'))

Summary

Where City has many Locations (HABTM association), create a child record like this:

a = Location.create(:name=>'Site 1', :city => City.create(:name=>'A City'))

Issue: a.city_id can't be changed in the database until a has been reloaded. Example:

a.city_id = 99
a.save  # => true
a.city_id # => 11

Details

class City < ActiveRecord::Base
  has_many :locations
end

class Location < ActiveRecord::Base
  belongs_to :city
end

a = Location.create(:name=>'Site 1', :city => City.create(:name=>'A City'))
# Create a record manually for comparison
b = Location.create(:name=>a.name, :city_id=>a.city_id)

a.city_id = 99
a.save  # => true
b.city_id = 99
b.save  # => true

a.city_id # => 11
b.city_id # => 99

Both a and b appear identical on creation but behave differently. Al开发者_如何学Gothough a is shown as valid after changing the city_id, and is saved successfully, the city_id is not changed.

However, if the record a is reloaded before it is changed, the change "works":

a = Location.create(:name=>'Site 1', :city => City.create(:name=>'A City'))
a.reload 
a.city_id = 99
a.save
a.city_id # => 99

This might seem rather arcane, since one can simply create the parent object first, but I discovered the issue using FactoryGirl,

factory :city do   
  name 'Jos'
end

factory :location do
  name 'Site 1'
  city { |a| a.association :city }
end

Factory(:location) shows the same behavior of having an unchangeable :city_id until it's reloaded from the database. It took a long time before I figured out what was going on, why my tests were failing because of the change not happening as I expected.

I see from inspecting the new location that it contains an entire city object, while after the reload it does not, so that must be a clue, but what exactly is going on here? Would it be reasonable to expect FactoryGirl to automatically reload the object before returning, or would that break something? Of course I could do that with a callback, but I'm interested in simplicity.

(If you wonder why I don't just say location.city = new_city rather than location.city_id = 99, it's because I was writing a test and wanted to create "orphan" records with invalid references.)

0

精彩评论

暂无评论...
验证码 换一张
取 消