Search code examples
ruby-on-railsruby-on-rails-4ruby-on-rails-5single-table-inheritance

Where to write associations in Single Table Inheritance in rails?


I have a base class called User and it has 2 subclasses named Manager and Creator. I have implemented Single Table Inheritance for User, Manager and Creator models.
There are 2 other models named Project and Bug.

class User < ApplicationRecord
end

class Manager < User
end

class Creator < User
end

class Project < ApplicationRecord
end

class Bug < ApplicationRecord
end

The associations I want to have are

  • A Project can have many Users
  • A Manager can have many Projects
  • A Creator can create many Bugs

I understand the relations but I am unable to understand where to put the associations(for the User and it's subclasses).
For example
For the association: A Creator can create many Bugs

class Creator < User
   has_many :bugs
end

Where do I put the other side of the association declaration? Do I write it inside the User class or the Creator class?
i.e

class Bug < ApplicationRecord
   belongs_to :user
end

or

class Bug < ApplicationRecord
   belongs_to :creator
end

Which one is the right way to do it?
The bottom line of the question is:
In Single Table Inheritance do I make associations(of other models) with the parent model or the child model?


Solution

  • In response to your comment, below is a potential way to model the associations you enumerate using only User, Project, Bug, and ProjectUser without STI.

    A simple way to model A Creator can create many Bugs might be something like:

    class User < ApplicationRecord
      has_many :bugs, foreign_key: :creator_id
    end
    

    And for your Bug model:

    # == Schema Information
    #
    # Table name: bugs
    #
    #  id                     :integer          not null, primary key
    #  creator_id             :integer
    #  created_at             :datetime         not null
    #  updated_at             :datetime         not null
    #
    class Bug < ApplicationRecord
      belongs_to :creator, class_name: 'User'
    end
    

    In this case, the role is implied by the association definition, but you don't have an explicit Role model. So if you have an instance of @user, you could do something like:

    @user.bugs.create bug_attributes
    

    If you have an @bug, then you can do:

    @bug.creator
    

    ...and get back the User that created the Bug.

    Similarly, to model A Manager can have many Projects, you could do something like:

    class User < ApplicationRecord
      has_many :bugs, foreign_key: :creator_id
      has_many :managed_projects, class_name: 'Project', foreign_key: :manager_id
    end
    
    # == Schema Information
    #
    # Table name: projects
    #
    #  id                     :integer          not null, primary key
    #  manager_id             :integer
    #  created_at             :datetime         not null
    #  updated_at             :datetime         not null
    #
    class Project < ApplicationRecord
      belongs_to :manager, class_name: 'User'
    end
    

    Again, the role is implicit to the association definition. And, with @user, you can do:

    @user.managed_projects.create project_attributes
    

    To model A Project can have many Users (assuming that a User can belong to many projects), you could use a ProjectUser model that might look something like:

    # == Schema Information
    #
    # Table name: project_users
    #
    #  id                     :integer          not null, primary key
    #  project_id             :integer
    #  user_id                :integer
    #  created_at             :datetime         not null
    #  updated_at             :datetime         not null
    #
    class ProjectUser < ApplicationRecord
      belongs_to :project
      belongs_to :user
    end
    

    Then in your Project, you could do:

    class Project < ApplicationRecord
      belongs_to :manager, class_name: 'User'
      has_many :project_users
      has_many :users, through: :project_users
    end
    

    And in your User, you could do:

    class User < ApplicationRecord
      has_many :bugs, foreign_key: :creator_id
      has_many :managed_projects, class_name: 'Project', foreign_key: :manager_id
      has_many :project_users
      has_many :projects, through: :project_users
    end
    

    Now you can do things like @project.users and @user.projects.

    Depending on your domain and use cases, you might want to make Role an explicit class. But, that's a whole other kettle of fish.