Belongs_to and callbacks

I have an Order, which belongs to a Buyer. Part of the order#form
allows the user to enter the email address of the buyer. If they email
address doesn’t exist in the system, then a Buyer record is created in
a before_save filter.

The weird thing is I have to explicitly set the buyer_id field in a
callback filter. That’s telling me that I’m either doing something
wrong, or Rails isn’t doing what it should. Here’s the sanitized code:

class Order < ActiveRecord::Base
attr_accessor :ordered_by

belongs_to :user
belongs_to :buyer, :class_name => “User”, :foreign_key => “buyer_id”

before_save :assign_buyer_by_email

private

def assign_buyer_by_email
self.buyer = User.find_by_email(ordered_by)
if buyer.nil?
self.buyer = User.create(:email => ordered_by)
# snip
self.buyer_id = self.buyer.id # Won’t work without this line,
seems un-DRY or worse
end
end
end

ordered_by is the email address on the order#form. You’ll notice buyer
shares the table with user. To head off questions about
find_or_create_by, I have other things do to the new user where you
see snip, and it also results in the user being created but the
buyer_id is not set.

Can someone explain what’s happening here?

On Jan 7, 4:04pm, IAmNan [email protected] wrote:

ordered_by is the email address on the order#form. You’ll notice buyer
shares the table with user. To head off questions about
find_or_create_by, I have other things do to the new user where you
see snip, and it also results in the user being created but the
buyer_id is not set.

What’s the value of self.buyer_id before you force it ?

Fred

At the point

If I stop in the debugger at
self.buyer_id = self.buyer.id # Won’t work without this line, seems
un-DRY or worse

Then…
self.buyer has a valid :id
self.buyer_id is nil

On Jan 7, 12:34pm, Frederick C. [email protected]

On Jan 8, 5:20pm, Frederick C. [email protected]
wrote:

On Jan 7, 5:03pm, IAmNan [email protected] wrote:> At the point

If I stop in the debugger at
self.buyer_id = self.buyer.id # Won’t work without this line, seems
un-DRY or worse

I’d try stepping through the assignment in self.buyer = foo. In theory
you should end up in BelongsToProxy#replace which in turn should be
setting buyer_id

Shouldn’t this happen only after you save the initial object?
I mean that at the time you do self.buyer = User.create … it creates
a new associated object in the memory as an attribute of self, but
buyer_id still doesn’t have that information because at the time you
fetched a self object into the memory it just didn’t exist.
I think if you do self.save (in your example it should be only for
testing purposes because it’s already inside the save callback) and
self.reload after self.buyer = User.create you should see a value
assigned.

Thanks, Ivan Povalyukhin

On Jan 9, 12:44pm, ivanpoval [email protected] wrote:

you should end up in BelongsToProxy#replace which in turn should be
setting buyer_id

Shouldn’t this happen only after you save the initial object?
I mean that at the time you do self.buyer = User.create … it creates
a new associated object in the memory as an attribute of self, but
buyer_id still doesn’t have that information because at the time you
fetched a self object into the memory it just didn’t exist.

Depends on the type of relationship and whether what you’re setting
the object to is already saved. Here we’re using the return value of
User.create, so it should be a saved object, so you just need to take
the id of that object and stick it in buyer_id. If the relationship
had been as one then we’d have to wait until the Order object had an
id.

Fred

I think if you do self.save (in your example it should be only for

On Jan 7, 5:03pm, IAmNan [email protected] wrote:

At the point

If I stop in the debugger at
self.buyer_id = self.buyer.id # Won’t work without this line, seems
un-DRY or worse

I’d try stepping through the assignment in self.buyer = foo. In theory
you should end up in BelongsToProxy#replace which in turn should be
setting buyer_id

Fred