Naming an association in Rails

Naming an association in Rails

In which we thwart the magic of Ruby on Rails.

The automagically generated and named rails associations (:has_many, :belongs) are handy, but I ran into problems when trying to give an association a name other than the default based on the class name (e.g. "users"). It would be better to use more informative names, especially where models may relate in a number of ways (e.g. "authors", "editors", "artists").

Unfortunately the Rails documentation is a little opaque on this, with many method options that _look_ as if they'll do the job (:source, :class_name, :foreign_key). Furthermore, there's are lots of partial solutions that partially work, making it hard to de-engineer what's going on.

This is one possible solution, which can be summarized:

  • Rename the association in the owning (has_many) model.
  • Rename the association in intermediate model and give the class name of the model on the other side of the relationship.

Note: this focuses on :has_many, because Hobo does not make full use of :has_many_and_belongs_to_many as yet.

The situation: each shift can have several supervisors and each user can be a supervisor for multiple shifts, i.e. a classic many-to-many

Version 1: with automatically deduced names based on the model classes:

# The supervisors are available through "users" class Shift < ActiveRecord::Base has_many :shift_supervisors, :dependent => :destroy has_many :users, :through => :shift_supervisors end # intermediate table # the user is pointed at by "user", and the shift by "shift" class ShiftSupervisor < ActiveRecord::Base belongs_to :shift belongs_to :user end # The shifts are available through "shifts" class User < ActiveRecord::Base has_many :shift_supervisors, :dependent => :destroy has_many :shifts, :through => :shift_supervisors end

Version 2: renaming the association in Shift to something more logical:

# The supervisors are available through "supervisors", just by renaming the # association class Shift < ActiveRecord::Base has_many :shift_supervisors, :dependent => :destroy has_many :supervisors, :through => :shift_supervisors end # the user is pointed at by "supervisor", with explicit naming of the model class ShiftSupervisor < ActiveRecord::Base belongs_to :shift belongs_to :supervisor, :class_name => "User" end # unchanged class User < ActiveRecord::Base has_many :shift_supervisors, :dependent => :destroy has_many :shifts, :through => :shift_supervisors end