3

I am using Ruby on Rails v3.2.2. I have following model classes

class Country < ActiveRecord::Base
  has_many :regions, :foreign_key => 'country_id'
end

class Region < ActiveRecord::Base
  belongs_to :country, :foreign_key => 'country_id'
  has_many :cities, :foreign_key => 'region_id'
end

class City < ActiveRecord::Base
  belongs_to :region, :foreign_key => 'region_id'
end

and I would like to make a City belongs_to :country.

I know that the simplest way to make that is to add a country_id database table column to the City database table and to state related ActiveRecord Associations, this way:

class Country < ActiveRecord::Base
  # ...
  has_many :cities, :foreign_key => 'country_id'
end

class City < ActiveRecord::Base
  # ...
  belongs_to :country, :foreign_key => 'country_id'
end

However, in order to store less database data, I think I may "use" the data already stored in the Region table since a city belongs to a region which in turn belongs to a country (this implies that a city belongs to a country) but, in this case, I have no idea on how to properly state ActiveRecord Associations for City and Country so to "exploit" mentioned relationship informations implicitly present "through" the Region model class.

How should I proceed?


Note: I am "forcing" to state the belongs_to :country ActiveRecord Association in the City model class because I would like to use the RoR :counter_cache feature (available only for belongs_to associations) in order to count cities present in a country.

Backo
  • 18,291
  • 27
  • 103
  • 170

2 Answers2

1

According to the rails documentation, you can specify a :through option on a has_one relation:

:through

Specifies a Join Model through which to perform the query. Options for :class_name, :primary_key, and :foreign_key are ignored, as the association uses the source reflection. You can only use a :through query through a has_one or belongs_to association on the join model.

So, what you want is to add has_one :country, :through => :region to City.

Community
  • 1
  • 1
cdesrosiers
  • 8,862
  • 2
  • 28
  • 33
  • I would like to state the `belongs_to :country` in `City` because I would like to use the RoR [`:counter_cache`](http://guides.rubyonrails.org/association_basics.html#belongs_to-counter_cache) feature in order to count cities present in a country. – Backo Nov 04 '12 at 23:17
  • I see. Unless someone else suggests a way to do this natively in rails, you may have to write this functionality yourself. – cdesrosiers Nov 04 '12 at 23:30
1

Use the :through option. As I've seen in your comment in the answer below (which by the way, was correct), you'll just have to add this:

has_one :country, :through => :region

to your City class. If you want to apply counter_cache on country for the cities, then you'll have to establish the relationship in the country class as well, like this:

has_many :cities, :through => :regions

and then you can have your count column

ChuckE
  • 5,610
  • 4
  • 31
  • 59
  • By using your code, "where" / "for which association" I should state the `:counter_cache` option? – Backo Nov 04 '12 at 23:31
  • You stated you wanted to count the cities of a country, then I would set the counter_cache option for the has_many cities relationship – ChuckE Nov 04 '12 at 23:38
  • and I would define the counter cache myself. Only belongs_to associations support it (which is a rails flaw), and for that reason I would set the an update callback for the country to update its cities cache. http://stackoverflow.com/questions/5256897/counter-cache-with-has-many-through – ChuckE Nov 04 '12 at 23:42