John Wells
2006-Feb-06 20:21 UTC
[Rails] Delaying initialization of associations until first access
Guys, Say I have the following: ----- class Person < ActiveRecord::Base has_one :house end class House < ActiveRecord::Base belongs_to :person attr_accessor :color end ----- Then I have the following code: ----- john = Person.new john.house.color = "Blue" john.save ----- What I would like to have happen, is that on first call to john.house, if house hadn''t been initialized a House object would be created and assigned to the house field. I''ve struggled to get this to work. Some have suggested overriding Person#save to make sure a House record was created everytime I created a Person record, but I really, really don''t want to do this. If I have 100000 Person objects, and only 20% of them own a house, all those empty House records in the database is a lot of waste. I''ve tried variations on the below code, each of which has failed to work. It *seemed* to work, but it doesn''t persist to the database properly: class Person < ActiveRecord::Base has_one :house def house if !read_attribute(:house) h = House.new h.save write_attribute(:house, h) end read_attribute(:house) end end Can anyone tell me the proper way to accomplish this behavior? Thanks! John
Nick Stuart
2006-Feb-06 20:30 UTC
[Rails] Delaying initialization of associations until first access
What exactly doesn''t get saved properly? From the looks of it you are not setting the house''s person property, which leads me to think that the person_id isn''t getting set in the database? Yes? No? So, before your h.save call, just add a h.person = self. Or you could just create and save all in one step. Your total method would be: def house return House.create(:person_id => self.id) if !read_attribute(:house) read_attribute(:house) end Just have the create call, no need to write the attribute or assign it anything else, everything should work just fine like that. I think this will work, but its untested ;) -Nick On 2/6/06, John Wells <lists@sourceillustrated.com> wrote:> Guys, > > Say I have the following: > ----- > class Person < ActiveRecord::Base > has_one :house > end > > class House < ActiveRecord::Base > belongs_to :person > attr_accessor :color > end > ----- > Then I have the following code: > ----- > john = Person.new > john.house.color = "Blue" > john.save > ----- > What I would like to have happen, is that on first call to john.house, if > house hadn''t been initialized a House object would be created and assigned > to the house field. > > I''ve struggled to get this to work. Some have suggested overriding > Person#save to make sure a House record was created everytime I created a > Person record, but I really, really don''t want to do this. If I have > 100000 Person objects, and only 20% of them own a house, all those empty > House records in the database is a lot of waste. > > I''ve tried variations on the below code, each of which has failed to work. > It *seemed* to work, but it doesn''t persist to the database properly: > > class Person < ActiveRecord::Base > has_one :house > > def house > if !read_attribute(:house) > h = House.new > h.save > write_attribute(:house, h) > end > read_attribute(:house) > end > end > > Can anyone tell me the proper way to accomplish this behavior? > > Thanks! > John > > _______________________________________________ > Rails mailing list > Rails@lists.rubyonrails.org > http://lists.rubyonrails.org/mailman/listinfo/rails >
John Wells
2006-Feb-06 21:25 UTC
[Rails] Delaying initialization of associations until first access
Nick Stuart said:> So, before your h.save call, just add a h.person = self. Or you could > just create and save all in one step. Your total method would be: > def house > return House.create(:person_id => self.id) if !read_attribute(:house) > read_attribute(:house) > endYes, I''ve tried this in two forms: def house if !read_attribute(:house) h = House.new h.person = self # second form tried was # h.person_id = self.id h.save write_attribute(:house, h) end read_attribute(:house) end Both "seem" to work, and the House record is created. However, if I then do: someperson.house.color = "Red" someperson.save I look in my logs, and the person record is being updated but the house record is not. Then, if I reload the person:>> someperson = Person.find(1) >> someperson.house.color=> nil Examining the logs, it seems that in "if !read_attribute(:house)", read_attribute(:house) never tests to true, *even* if the record already exists. This is counter intuitive and bewildering. Any ideas what I may be doing wrong? Thanks! John
John Wells
2006-Feb-06 21:25 UTC
[Rails] Delaying initialization of associations until first access
Nick Stuart said:> So, before your h.save call, just add a h.person = self. Or you could > just create and save all in one step. Your total method would be: > def house > return House.create(:person_id => self.id) if !read_attribute(:house) > read_attribute(:house) > endYes, I''ve tried this in two forms: def house if !read_attribute(:house) h = House.new h.person = self # second form tried was # h.person_id = self.id h.save write_attribute(:house, h) end read_attribute(:house) end Both "seem" to work, and the House record is created. However, if I then do: someperson.house.color = "Red" someperson.save I look in my logs, and the person record is being updated but the house record is not. Then, if I reload the person:>> someperson = Person.find(1) >> someperson.house.color=> nil Examining the logs, it seems that in "if !read_attribute(:house)", read_attribute(:house) never tests to true, *even* if the record already exists. This is counter intuitive and bewildering. Any ideas what I may be doing wrong? Thanks! John
Nick Stuart
2006-Feb-07 01:44 UTC
[Rails] Delaying initialization of associations until first access
On 2/6/06, John Wells <lists@sourceillustrated.com> wrote:> Nick Stuart said: > > So, before your h.save call, just add a h.person = self. Or you could > > just create and save all in one step. Your total method would be: > > def house > > return House.create(:person_id => self.id) if !read_attribute(:house) > > read_attribute(:house) > > end > > Yes, I''ve tried this in two forms: > > def house > if !read_attribute(:house) > h = House.new > h.person = self > # second form tried was > # h.person_id = self.id > h.save > write_attribute(:house, h) > end > read_attribute(:house) > end > > Both "seem" to work, and the House record is created. However, if I then do: > > someperson.house.color = "Red" > someperson.saveYou are going to need to save your house model seperatly from your person model. But! If you always want to do this, through in a before_save filter for the person class that automatically saves the house model at the same time. Should be simple enough.> > I look in my logs, and the person record is being updated but the house > record is not. Then, if I reload the person: > > >> someperson = Person.find(1) > >> someperson.house.color > => nil > > Examining the logs, it seems that in "if !read_attribute(:house)", > read_attribute(:house) never tests to true, *even* if the record already > exists. This is counter intuitive and bewildering.For this, try using `attribute_present?` instead. Seems to be built for what you are trying to do. Haven''t used it personally so not sure. Just dug it out of the API. Hope this helps! -Nick> > Any ideas what I may be doing wrong? > > Thanks! > > John > > _______________________________________________ > Rails mailing list > Rails@lists.rubyonrails.org > http://lists.rubyonrails.org/mailman/listinfo/rails >
John Wells
2006-Feb-07 14:38 UTC
[Rails] Delaying initialization of associations until first access
On Monday, February 06, 2006, at 8:44 PM, Nick Stuart wrote:>You are going to need to save your house model seperatly from your >person model. But! If you always want to do this, through in a >before_save filter for the person class that automatically saves the >house model at the same time. Should be simple enough.This part works...when I set the before_save filter, the House is indeed saved to the database. However:>For this, try using `attribute_present?` instead. Seems to be built >for what you are trying to do. Haven''t used it personally so not sure. >Just dug it out of the API.Switched it to attribute_present?...same behavior. Isn''t surprising since attribute_present? uses read_attribute under the covers. So, while we''re successful in writing to the database, when you reload the Person object ActiveRecord for some reason doesn''t see the connection and attribute_present? resolves false, therefore creating another entry in the table. I am at a complete loss. The code now looks like this: def before_save self.house.save end def part_count if !attribute_present?("house") h = House.new h.color = "blue" h.person = self write_attribute("house", h) end read_attribute("house") end Any other thoughts? -- Posted with http://DevLists.com. Sign up and save your time!
John Wells
2006-Feb-07 14:40 UTC
[Rails] Delaying initialization of associations until first access
On Monday, February 06, 2006, at 8:44 PM, Nick Stuart wrote:>You are going to need to save your house model seperatly from your >person model. But! If you always want to do this, through in a >before_save filter for the person class that automatically saves the >house model at the same time. Should be simple enough.This part works...when I set the before_save filter, the House is indeed saved to the database. However:>For this, try using `attribute_present?` instead. Seems to be built >for what you are trying to do. Haven''t used it personally so not sure. >Just dug it out of the API.Switched it to attribute_present?...same behavior. Isn''t surprising since attribute_present? uses read_attribute under the covers. So, while we''re successful in writing to the database, when you reload the Person object ActiveRecord for some reason doesn''t see the connection and attribute_present? resolves false, therefore creating another entry in the table. I am at a complete loss. The code now looks like this: def before_save self.house.save end def part_count if !attribute_present?("house") h = House.new h.color = "blue" h.person = self write_attribute("house", h) end read_attribute("house") end Any other thoughts? -- Posted with http://DevLists.com. Sign up and save your time!
John Wells
2006-Feb-07 14:42 UTC
[Rails] Delaying initialization of associations until first access
Update...I have it working! I''m not sure why this works and our previous attempts didn''t (would love the explanation). greendale at rails.technoweenie.net provided this code, which worked: alias :_house :house def house _house or self.house = House.new end Now, that''s simple. I can''t understand when this worked and read_attribute didn''t? -- Posted with http://DevLists.com. Sign up and save your time!