Model with two associations?

Hello!

I’m stumped on this one and after lots of searching and reading I need
some help, please!

I’m trying to model a “purchase” as occurring between a “seller” and a
“buyer”, both of which are instances of a User class.

I have:

class User < ActiveRecord::Base
has_many :purchases, :as => :seller, :dependent => true
has_many :purchases, :as => :buyer, :dependent => true

end
class Purchase < ActiveRecord::Base
belongs_to :seller, :class_name => “User”, :foreign_key =>
“seller_id”
belongs_to :buyer, :class_name => “User”, :foreign_key => “buyer_id”

end

The users table doesn’t define any keys, but the purchases table is:

create_table “purchases” do |t|
t.column :seller_id, :integer, :null => false
t.column :buyer_id, :integer, :null => false
… (other columns)
end

The problem is that this isn’t working as expected. I tried some
polymorphic options, but I don’t think that’s what’s needed (although
I’m not sure, honestly). Do I need to change the model associations?
And if so, how? I know with polymorphic associations, I would set a
“seller_type” and “buyer_type” on the purchases table, but it’s not
polymorphic, is it?

Also, I realized I need some way to figure out for a given user what
purchases they had (a) in general, (b) as a seller and © as a buyer.
Assuming the associations are correct, I can do (a) via:
User.find(1).purchases, but how would I do (b) or ©?

This seems to me to be an easy and common thing, but I’ve been stumped
for a while (I’m not a Rails newbie, but I’ve cough, cough not been
keeping up with it, so I’m trying to learn all the new stuff since
Rails 1.0. DOH!)

Thanks!

-Dan

I think that items a person sells should not be called “purchases”, but
something more like sales.

You can’t have two association statements defined as the same thing
either, I think the latter overrides the former.

Try this:

has_many :sales, :class_name => “purchases”, :as => :seller, :dependent
=> true
has_many :purchases, :as => “buyer”, :dependent => “true”

Ryan,

Thanks for the attempt. It didn’t work, though. Unless I have the
“purchases” class set wrong. If I manually create a record in the DB,
with two users and one purchase, one as the seller and one as the
buyer… and then do:

User.find(1).sales
or
User.find(1).purchases

I get an error that it is looking for "purchases.seller_type or
purchases.buyer_type. But if they are both “User”, can’t that be set
somewhere rather than having seller_type and buyer_type fields on the
purchases table?

Actually, I tried setting the seller_type and buyer_type fields on the
purchases table and it sort-of works. If I manually create a purchase
entry and populate the fields in the DB record, and put “User” for the
type fields, then it works. But, if I create a new purchase and give
it two users, it creates the record but puts “NULL” in the purchases
table for the seller_type and buyer_type. How would these get set to
“User”? Or does that need to happen manually?

-Dan

Try using :foreign_key => “buyer_id” and :foreign_key => “seller_id”
instead then.

On the User class? or the Purchase class? I tried it on both and it
didn’t seem to make a difference… still created the type fields as
NULL.

Hmmm… I guess I could explicitly set them: purchase.seller_type =
“User” (for example)

But it seems like there’s gotta be a cleaner way to set 'em up and
have that happen automagically.

:slight_smile:

-Dan

Danimal wrote:

Hello!

I’m stumped on this one and after lots of searching and reading I need
some help, please!

I’m trying to model a “purchase” as occurring between a “seller” and a
“buyer”, both of which are instances of a User class.

I have:

class User < ActiveRecord::Base
has_many :purchases, :as => :seller, :dependent => true
has_many :purchases, :as => :buyer, :dependent => true

end
class Purchase < ActiveRecord::Base
belongs_to :seller, :class_name => “User”, :foreign_key =>
“seller_id”
belongs_to :buyer, :class_name => “User”, :foreign_key => “buyer_id”

end

The users table doesn’t define any keys, but the purchases table is:

create_table “purchases” do |t|
t.column :seller_id, :integer, :null => false
t.column :buyer_id, :integer, :null => false
… (other columns)
end

The problem is that this isn’t working as expected. I tried some
polymorphic options, but I don’t think that’s what’s needed (although
I’m not sure, honestly). Do I need to change the model associations?
And if so, how? I know with polymorphic associations, I would set a
“seller_type” and “buyer_type” on the purchases table, but it’s not
polymorphic, is it?

Also, I realized I need some way to figure out for a given user what
purchases they had (a) in general, (b) as a seller and (c) as a buyer.
Assuming the associations are correct, I can do (a) via:
User.find(1).purchases, but how would I do (b) or (c)?

This seems to me to be an easy and common thing, but I’ve been stumped
for a while (I’m not a Rails newbie, but I’ve cough, cough not been
keeping up with it, so I’m trying to learn all the new stuff since
Rails 1.0. DOH!)

This is ye olde self-referential has_many :through. You’re on the right
track, but you got tripped up on the :as thing. I’ve got a write-up
that should explain what to do here:
http://blog.hasmanythrough.com/2007/10/30/self-referential-has-many-through


Josh S.
http://blog.hasmanythrough.com

Josh,

Is your example only for Rails 2.0? I’m on 1.2.5, although I probably
should just get the 2.0 RC I guess.

-Dan

Haha! Yup, jumped on 2.0 RC and it works just fine. Well, this app is
a long way out from production, so I guess I’ll keep it on 2.0.

Nice!

Thanks, all!

User.find(:first).sales.first # => the first purchase object. Whee!

-Dan

DOH!

Josh, I’ve read your stuff a TON! (It’s really helped me catch up with
Rails) I can’t believe I didn’t think of that very post. I’m gonna go
refactor the code that way as it looks like exactly what I need.

Thanks!

-Dan

Danimal wrote:

Haha! Yup, jumped on 2.0 RC and it works just fine. Well, this app is
a long way out from production, so I guess I’ll keep it on 2.0.

Nice!

Thanks, all!

User.find(:first).sales.first # => the first purchase object. Whee!

-Dan

Cool, glad it worked. And yeah, 2.0 is the way to go!


Josh S.
http://blog.hasmanythrough.com