Is polymorphic has_one/has_many possible? (in addition to polymorphic belongs_to)

Hello,

i wonder if the following is possible in Rails (seems like not), and if
not, whether such database structure is discouraged for some good
reason.

I roughly want to have

class Payment < ActiveRecord::Base
has_one :purchase
end

where purchase can be either TicketPurchase, or MembershipPurchase, so
to have also

class TicketPurchase < ActiveRecord::Base
belongs_to :payment
end

class MembershipPurchase < ActiveRecord::Base
belongs_to :payment
end

so things need to be polymorphic.
I want this correspond to the following database structure:

payments: id | amount | purchase_type
ticket_purchases: id | number | payment_id
membership_purchases: id | membership_type | payment_id

This does not seem to be possible in Rails, to use polymorphic
associations i would need to store the foreign key in payments table.
Am i right that it is not possible, and is there a good reason for
discouraging such associations?

Thank you,

Alexey.

Hi Alexey,

On Fri, May 6, 2011 at 5:21 AM, Alexey M. [email protected]
wrote:

end
end
Depending on your constraints, i.e., whether or not you have control
of the schema, you might flip the association.

class TicketPurchase < ActiveRecord::Base
has_one :payment # this could just as easily be a has_many if you
need to support split tenders
end

class MembershipPurchase < ActiveRecord::Base
has_one :payment
end

class Payment < ActiveRecord::Base
belongs_to :ticket_purchase
belongs_to :membership_purchase
end

Alternatively, or perhaps in addition, the purchase hierarchy your
explanation touches on looks like a pretty classic use-case for STI
(P of EAA: Single Table Inheritance)

HTH,
Bill

Thanks Bill,
i am aware of the alternatives, but i was wondering if i can keep this
database schema which i like for that case, and to still be able to use

payment = Payment.find(1)
puts payment.purchase.inspect

so that payment.purchase was either TicketPurchase or
MembershipPurchase, while the foreign keys were stored in purchases
tables.

Alexey.

Hi Alexey,

On Fri, May 6, 2011 at 6:59 AM, Alexey M. [email protected]
wrote:

Thanks Bill,
i am aware of the alternatives, but i was wondering if i can keep this
database schema which i like for that case, and to still be able to use

payment = Payment.find(1)
puts payment.purchase.inspect

so that payment.purchase was either TicketPurchase or
MembershipPurchase, while the foreign keys were stored in purchases
tables.

The problem you’re up against is that relational databases don’t
support inheritance. So you’d either need to use:

payment.purchase.membership_purchase.inspect

or move to an STI model for purchases and drop the ticket_purchases
and membership_purchases tables altogether if you want / need to be
able to get any ‘meaningful’ information from payment.purchase.inspect

Google ‘rails sti’ for some good links on how Rails implements /
supports STI.

Best regards,
Bill

Bill, i see no theoretical difficulty in implementing what i want, i was
just wondering why it is not implemented in Rails (whether it is
considered a “bad style” for some reason).

The same way as one does

class Payment < ActiveRecord::Base
belongs_to :purchase, :polymorphic => true
end

(and then one can use payment.purchase regardless of the type of
purchase)
it would be natural to expect that

class Payment < ActiveRecord::Base
has_one :purchase, :polymorphic => true
end

also be possible, the difference is in storing the foreign key on the
other side.
Maybe it is considered redundant because one can do

class Payment < ActiveRecord::Base
has_one :ticket_purchase
has_one :membership_purchase
end

but this way one has to first look up payment.purchase_type, and then
use either payment.ticket_purchase or payment.membership_purchase
accordingly.

Best regards,

Alexey.

Of course i can also do

class Payment < ActiveRecord::Base
has_one :ticket_purchase
has_one :membership_purchase
def purchase
case purchase_type
when “TicketPurchase” then ticket_purchase
when “MembershipPurchase” then membership_purchase
end
end
end

Alexey.

Frederick,

because the “purchase_type” column would still be in the “payments”
table, like in usual polymorphic association.

I am in fact questioning myself if there are any benefits over the usual
polymorphic association.
I thought at first that it would be nice to be able to delete purchases
tables if necessary (to clear for example member’s purchase history) and
not to have to have anything to do with the payments table (with usual
polymorphic association i would need to at least nullify the foreign
keys), and also i would only need to impose uniqueness constraints on
foreign keys, and not on the pair [type, foreign key].
But on the other hand, i would not be able to ensure that there are no
references from two different “purchases” tables to the same record in
“payments” table…
So i am hesitating.

Alexey.

On 6 May 2011, at 15:44, Alexey M. [email protected] wrote:

(and then one can use payment.purchase regardless of the type of
purchase)
it would be natural to expect that

class Payment < ActiveRecord::Base
has_one :purchase, :polymorphic => true
end

also be possible, the difference is in storing the foreign key on the
other side.

How would activerecord know what tables to look at?

Fred