6

I have two tables:

currencies and rates

currencies: id:int, code:string, name: string

rates: id:int, top_currency_id:int, bottom_currency_id:int, rate:float

And I have two active records for them:

class Rate < ActiveRecord::Base
  attr_accessible :bottom_currency, :rate, :top_currency, :top_currency_id

  belongs_to :top_currency, :class_name => 'Currency', :foreign_key => 'top_currency_id'
  belongs_to :bottom_currency, :class_name => 'Currency', :foreign_key => 'bottom_currency_id'
end


class Currency < ActiveRecord::Base
  attr_accessible :code, :name

  has_many :rates
end

So the problem is: When I'm tring to execute following code: top_currency = Currency.find_by_id(1) @test = Rate.where(:top_currency=>top_currency)

I getting following error:

Mysql2::Error: Unknown column 'rates.top_currency' in 
'where clause': SELECT `rates`.* FROM `rates`  WHERE `rates`.`top_currency` = 1

Why Rails's magic doesn't work?

Many thanks.

shweta
  • 8,019
  • 1
  • 40
  • 43
Grisha
  • 77
  • 1
  • 1
  • 7
  • The error states that `top_currency` isn't a column in the `rates` table, are you sure you migrated your changes to the environment you're using? – JaredMcAteer Feb 24 '13 at 20:14
  • @JaredMcAteer I have top_currency_id column, and I supposed that Rails should look for top_currency_id column insted of top_currency. – Grisha Feb 24 '13 at 20:17

3 Answers3

7

In your two belongs_to methods, change the foreign_key option to primary_key, leaving everything else as is.

belongs_to :top_currency, :class_name => 'Currency', :primary_key => 'top_currency_id'
# ...

By default, an associated object's primary key is id. However, your currency model has three primary keys, the expected id plus two extra keys: top_currency_id and bottom_currency_id. Active Record needs to know which key to look for. Tell it with the primary_key option.

The foreign_key option is needed when a foreign key is different than the association's name (belongs_to :name) plus "_id". Since your foreign key matches the association name plus "_id," you do not need to use the foreign_key option.

Substantial
  • 6,684
  • 2
  • 31
  • 40
5

From what I see, your code should work in theory. But I do think you are being a bit redundant.

It should be enough to just do this:

class Rate < ActiveRecord::Base
  belongs_to :top_currency, class_name: 'Currency'
  belongs_to :bottom_currency, class_name: 'Currency'
end

Rails will infer that the foreign key for top_currency is top_currency_id, and bottom_currency_id for bottom_currency.

Jesper
  • 4,535
  • 2
  • 22
  • 34
0

I don't think you can query on the relationship like that. To use your example:

top_currency = Currency.find_by_id(1)
@test = Rate.where(:top_currency=>top_currency)

You'd have to change it to this:

top_currency = Currency.find_by_id(1)
@test = Rate.where(:top_currency_id => top_currency.id)

But it might just be easier to do this:

top_currency = Currency.find_by_id(1)
@test = top_currency.rates
seanhussey
  • 379
  • 3
  • 10
  • Yes, `@test = Rate.where(:top_currency_id => top_currency.id)` even `@test = Rate.where(:top_currency_id => top_currency)` works well. But I want to make `Rate.where(:top_currency=>top_currency)` works – Grisha Feb 25 '13 at 05:36
  • Just curious as to why are you so committed on having `Rate.where(:top_currency=>top_currency)` when the association you set up gives you the method `top_currency.rates`to do the same thing? – rocket scientist Feb 25 '13 at 14:16
  • @MollyStruve in fact I need to execute following request: `Rate.where(:top_currency=>top_currency, :bottom_currency=>bottom_curency)` – Grisha Feb 25 '13 at 14:45
  • To my knowledge, you can't do that. Add _id to each of the symbols and it'll work. I see what you're going for, but it's not possible in a simple query. You're looking to do a join, which is much more expensive than simply checking for the id of the related records. – seanhussey Feb 25 '13 at 15:33