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,

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

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,

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.

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.

How would activerecord know what tables to look at?

Fred

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.