1
votes

I’m new to programming and have difficulty determining the model association I need. I already have two tables: Organizations and Users with a 1:many relationship, since an organization has multiple users/members. Not all users are a member of an organization though but that’s not a problem.

# In the organization model:
has_many :users

# In the user model:
belongs_to :organization, inverse_of: :users

# In the users migration file:
t.references :organization, index: true, foreign_key: true

I need to extend this with a moderator function: A user can be a moderator for multiple organizations and an organization can have multiple users as their moderator. So there will then be two separate reasons for a relationship between the user and organization model (user as a member of an organization and user as a moderator of an organization; no need to a member before you can be a moderator).

I read here about the two different possible ways to create the association, namely has_many :through or has_and_belongs_to_many.

The simplest rule of thumb is that you should set up a has_many :through relationship if you need to work with the relationship model as an independent entity. If you don't need to do anything with the relationship model, it may be simpler to set up a has_and_belongs_to_many relationship (though you'll need to remember to create the joining table in the database). You should use has_many :through if you need validations, callbacks, or extra attributes on the join model.

I'm not sure what is meant by "if you need to work with the relationship model as an independent entity". In my use case is a moderator will have additional rights. The association will, I belief, be used in two ways: 1) displaying the moderators for an organization, and displaying the organizations that a user is the moderator of; and 2) look up if a user has moderator rights to allow him these rights. So I don't belief I will need validations (organization will itself be able to select users as moderator) or extra attributes. I'm not sure about callbacks (don't know exactly what they are).

So in short, which one of the two forms of association is the best association in my use case?

2

2 Answers

3
votes

"if you need to work with the relationship model as an independent entity" means you will be able to operate on the Relationship model as any other ActiveRecord model, i.e you can create, save, destroy etc. records or assign various types of attributes to the relationship.

Suppose, for example, that a user could have different types of relationship with the organization (e.g. moderator, employee, stockholder et). Using the :through association, you would have:

class Relationship < ActiveRecord::Base
  belongs_to :user
  belongs_to :organization
end

class User < ActiveRecord::Base
  has_many :organizations, through: relationships
end

class Organization < ActiveRecord::Base
  has_many :users, through: relationships
end

# schema.rb
ActiveRecord::Schema.define(:version => 20150511223747) do
   # . . . . 
   create_table "relationships", :force => true do |t|
      t.string   "relationship_type"
      t.integer  "user_id"
      t.integer  "organization_id"
      t.datetime "created_at"
      t.datetime "updated_at"
   end
end

# things you can do: 
org = Organization.first
moderators_or_employees = org.users.where("relationship_type = 'moderator' OR relationship_type = 'employee'")

new_moderators = org.users.where("relationship_type = 'moderator' AND DATE(relationships.created_at) > '2015-01-01'")

These are things you could not do with the has_and_belongs_to_many association since you have no way of querying on relationship type or created_at. Because of these limitations, I have often found that the :through relationship, although a bit more complicated, gives you a lot more flexibility.

2
votes

My nutshell explanation when people ask:

  • habtm – only a join table, e.g., two ids
  • hm:t – a join table with meta data about the join relationship, e.g., two ids and other data like times, counts, etc.