views:

74

answers:

3

In Ruby on rails, our model includes orders and payments. There's 1-many relationship between order and payments. In the orders model we specify: *has_many :payments, :as => :payable* And a payment record has payable_id that is set to order.id.

In a report, I want to select all payments that belong to orders of a given type. Using: payments = Payment.find(:all, :conditions => conditions) and adding 'payable.type="a" ' to the conditions doesn't work. It seems that ActiveRecord doesn't develop this into a correct join statement (payable_id=order.id and orders.type='a'). I cannot use explicit SQL here, as the condition contains other things that are inserted there earlier in the code. Thanks, Raffi Lipkin

+1  A: 

Try incliding and reference the actual table id name in the condition, rather than the association alias:

find(:include => "payments", :conditions => ["payment.type = ?", "x"]
Toby Hede
A: 

You mention 'payment type'. If they're fairly static, have you considered using single table inheritance (STI) to subclass your different payment types? Then Rails will do all the magic to filter on type.

E.g.

class CreditCardPayment < Payment
...
end

It doesn't even need to exhibit different behaviour initially; however you'll probably find that it turns out to be really useful to have different data and polymorphic behaviour around payments.

Julian
+2  A: 

Your conditions clause is wrong.

You state that an Order

has_many :payments, :as => :payable

This tells me that a Payment

belongs_to :payable, :polymorphic => true

This means that the payments table has two columns of note: payable_id and payable_type. This also means that Payments can be applied not just to Orders, but also to other models as well (CreditCardBalances, who knows).

If you want to query for payments of a specific type, i.e. belonging to any instance of a particular class, you need to be querying the field payments.payable_type. This works fine:

Payment.find(:all, :conditions => "payable_type = 'Order'")

Here's a gist that shows what I did to test this. The models created are set up just like described above.

Don't forget that you can extract that into named scopes if it's easier:

named_scope :on_orders, :conditions => "payable_type = 'Order'"

Which makes it

Payment.on_orders

Or dynamically:

named_scope :on, lambda { |type| { :conditions => "payable_type = '#{type.to_s}'" } }

Which then makes it

Payment.on(Order) # or Payment.on(CreditCardBalance) or Payment.on("Order")
Ian Terrell
Thanks. You are right. Payments can belong to other types.I'll try to add payment.type in the condition.
Raffi Lipkin
Sigh. It's not "payment.type". Look at it in the database. It's "payments.payable_type".
Ian Terrell