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?

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

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

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.

> On Jan 7, 5:03 pm, IAmNan <dger...@gmail.com> 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.

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