Associations are great in Rails. There is a ton of functionality baked right in. One such feature is the ability to change the name of association columns.

This is the first post in a series

First, lets talk through some setup data. We are working with two models: Journal and User. And their respective association:

Your models currently look like the following:

# cmd: rails generate model Journal
# app/models/journal.rb
class Journal < ActiveRecord::Base
  belongs_to :user
end

# cmd: rails generate model User
# app/models/user.rb
class User < ActiveRecord::Base
  has_many :journals
end

Assuming the above already exists, now we want to ensure a reference between the two models with a user_id column on the Journal. At this point you could use the following command to build that migration

rails generate migration AddUserToJournal user:references
rails db:migrate

Test driving this in a console should work with the above associations

# Inside Rails console
user = User.new(...)
journal = Journal.create(user: user)

journal.user #=> #<User id: 3, created_at: "2019-11-15 14:50:51", updated_at: "2019-11-15 14:50:51">

Who is the creator of the Journal?

The previous setup works as you would expect, but what if you wanted to better describe who created a Journal without changing the classname for User? You might want a column named creator that stores the user who created the journal. This would allow for more readable association language for future developers.

Rails has some built in support to achieve just this using some additional arguments within the belongs_to method. Note: this requires no changes to the User model.

# ~Can't use references since we are now aliasing to creator~
rails generate migration AddCreatorToJournal creator_id:integer

# (2019-03-07) Actually, the above isn't true. You can instead use `creator:references`
# and then fill in the proper configuration within the migration. See the follow-up post
# for more details: http://joshfrankel.me/blog/using-custom-named-association-columns-in-a-rails-migration/

# app/models/journal.rb
class Journal < ActiveRecord::Base
  belongs_to :creator, class_name: :User
end

user = User.new(...)
Journal.create(creator: user) # Check this out! Rails magic allows us to pass in a user as a creator

Our model now responds to the column name creator the same way it would to a standard user reference.

One step further

You can improve on this approach by adding an index and foreign key to the creator_id column in the migration. This ensures referential integrity as well as speeding up any queries based on creator_id.

# What the migration might look like
class AddCreatorToJournal < ActiveRecord::Migration
  def change

    # Notice how the index is for :creator but references users
    add_reference :journals, :creator, references: :users, index: true

    # Just like the belongs_to contained class_name: :User, the foreign key
    # also needs a specific custom column name as :creator_id
    add_foreign_key :journals, :users, column: :creator_id
  end
end

# app/models/journal.rb
class Journal < ApplicationRecord
  # Sets the specific name of the foreign key to the creator_id like in the
  # migration above
  belongs_to :creator, class_name: :User, foreign_key: :creator_id
end

# app/models/user.rb
class User < ApplicationRecord
  # We need to speciy what the foreign_key is for the opposite association
  # otherwise the User model won't know what column to join on in the SQL
  # statement.
  has_many :journals, foreign_key: :creator_id
end

Now you have a custom named column, that is constrained based on a foreign key , and indexed for faster querying. There are plenty of other tricks with associations that can also be found on Rails Guides.

« Previous Post
How to validate attributes for serialized objects
Next Post »
Fast Reports: Exporting to a CSV in Ruby

Join the conversation

comments powered by Disqus