Search code examples
ruby-on-railsactiverecordruby-on-rails-5form-for

Setting up State and Cities Rails Models, ActiveRecord Associations and Form


I wanted to know the best way to implement the following:

  • Users can create a Post and each Post is associated with a US City and State.

  • I wanted to have a form where there is a dropdown box that a user can select the State and select the relevant City for example: [Hollywood] [California].

I have setup a State and City model.

The State model:

# == Schema Information
#
# Table name: states
#
#  id         :integer          not null, primary key
#  name       :string           default(""), not null
#  short      :string           default(""), not null
#  created_at :datetime         not null
#  updated_at :datetime         not null
#

class State < ApplicationRecord
  has_many :cities
end

The City model:

# == Schema Information
#
# Table name: cities
#
#  id         :integer          not null, primary key
#  name       :string
#  state_id   :integer
#  created_at :datetime         not null
#  updated_at :datetime         not null
#
# Indexes
#
#  index_cities_on_state_id  (state_id)
#

class City < ApplicationRecord
  belongs_to :state
end

With the Post model

# == Schema Information
#
# Table name: posts
#
#  id              :integer          not null, primary key
#  title           :string           default(""), not null
#  body            :string           default(""), not null

class Post < ApplicationRecord
end

With the Post model I thought I would to a belongs_to and has_many association with references/foreign key setup like:

class Post < ApplicationRecord
  belongs_to :city
  belongs_to :state
end

class City < ApplicationRecord
  belongs_to :state
  has_many :posts
end

class State < ApplicationRecord
  has_many :cities
  has_many :posts
end

The view would use a form_for with a grouped_collection for cities and collection for the states but I don't think this is a good implementation because:

  1. Selecting the city already as the association with the state. It seems redundant having the user select both model options?
  2. The dropdown box in view will load 60000 city records and it will slow down the browser.

I wanted to know if a Polymorphic association would fit my use case and if someone can forward me in the right direction.


My thoughts: I was thinking something where the user selects the the State in a dropdown box eg. California, but enters the city in a text field and if the city does not exists in Cities table it create it, otherwise it will link to it?

Thanks for your help.


Solution

  • What you want is a has_one through: association. This indirect relation tells rails to join through another association and removes the need for a duplicated foreign key.

    class Post < ApplicationRecord
      belongs_to :city
      has_one :state, through: :city
    end
    

    To create a full hierarchy you would do it like so:

    class Post < ApplicationRecord
      belongs_to :city
      has_one :state, through: :city
      has_one :country, through: :state
    end
    
    class City < ApplicationRecord
      belongs_to :state
      has_one :country, through: :state
      has_many :posts
    end
    
    class State
      belongs_to :country
      has_many :cities
      has_many :posts, through: :cities
    end
    
    class Country < ApplicationRecord
      has_many :states
      has_many :cities, through: :states
      has_many :posts, through: :cities
    end
    

    Polymorphic associations are a very different thing altogether - its used when an association can be to different models. In this example a Comment can be belong to a Post or a Page.

    class Comment < ApplicationRecord
      belongs_to :commentable, polymorphic: true
    end
    
    class Post < ApplicationRecord
      # this tells AR to look at the `commentable` association on 
      # Comment. 
      has_many :comments, as: :commentable 
    end
    
    class Page < ApplicationRecord 
      has_many :comments, as: :commentable
    end