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
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
Nick S. 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.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 S. 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.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
On 2/6/06, John W. [email protected] wrote:
def house
Both “seem” to work, and the House record is created. However, if I then do:
someperson.house.color = “Red”
someperson.save
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.
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
On Monday, February 06, 2006, at 8:44 PM, Nick S. 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?
On Monday, February 06, 2006, at 8:44 PM, Nick S. 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?
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?