omninonsense
omninonsense

Reputation: 6882

Should I use `has_and_belongs_to_many` or `has_many :through` in my model?

I have read the Choosing Between has_many :through and has_and_belongs_to_many on the Rails website, however I am a bit confused since I have a different case to the ones given on the website.

I have two models: Prop and CharacterCostume, and the character's costume can have multiple props associated to it, but a prop doesn't belong to that character and it can be used by any number of characters in the scene, too.

Right now I have has_and_belongs_to_many :props inside my CharacterCostume model, which does exactly what I want it to do: it fetches all the props associated with the costume using a table named character_costumes_props when I call CharacterCostume#props

However the association name is putting me off because of the "belongs to many" part. The costume does not belong to any of the props, so there's no has_and_belongs_to_many :character_costumes inside the Prop model.

I know that it can all function fine without it, but it got me thinking that maybe I should use a has_many :through association, but that requires me to create a superfluous model (it is superfluous, right?) and the model would look like this:

class CharacterCostumeProp < ActiveRecord::Base
  belongs_to :character_costume
  has_one :prop
end

Also, would has_one instead of belongs_to work here?

I want the code to be as semantic as possible, but I am not sure if this will increase the requirement for resources or decrease performance in some way, since there's an intermediate model.

Are there certain quirks/benefits attached to either approach? Is mine good enough? Or is my thinking completely wrong from what I need to do?

Thanks!

Upvotes: 1

Views: 1432

Answers (3)

Narfanator
Narfanator

Reputation: 5803

I think you want to use a :has_many, :through because you're going to want to work directly with the relation model - what scene(s), consumed or damaged, etc.

But, the reason it reads funny to you is that, for the most part, has_many and belongs_to don't really mean what they mean in English. What they really mean is "They have the foreign keys" and "I have the foreign key", respectively; the exception being the :dependent => :destroy behavior.

That still doesn't really help with has_and_belongs_to_many, since you're then saying, "They have the foreign keys and I have the foreign keys` - except that you can think of it sort of adding a new part both to "I" and "They" that happens to be the same part for each, and has those keys.

Does that help?

Upvotes: 2

Marcelo De Polli
Marcelo De Polli

Reputation: 29281

The single most important question you must ask yourself when deciding between HABTM and has_many :through is this:

Do I want to store any information specific to the association?

Example 1: magazine subscriptions

A many-to-many relationship between readers and magazines might conceivably be structured as a HABTM or a has_many :through. However, the latter makes far more sense in this case because we can easily think of information specific to the association that we might want to store.

A reader is related to a magazine through a subscription, and every subscription can be described by fields such as price, starting date, issue frequency and whether it's active or not.

Example 2: tags

The relationship between an existing Tag model and, say, an Article model is clearly of the many-to-many kind. The fact that one particular tag has been associated to any particular article must have no influence on whether the same tag will be able to be similarly associated to other articles in the future.

But, differently from the previous example, here the association itself is all the information we need. We just need to know which tags are associated to any given article. It doesn't matter when the association was formed. It doesn't matter how long it lasted.

It may matter to us how many articles a tag is associated with. But that information is stored in the Tag model since it's not specific to an association. There is even a Rails feature that takes care of that called counter_cache.

Upvotes: 2

mathieugagne
mathieugagne

Reputation: 2495

  1. has_one wouldn't work, you'd need belongs_to
  2. it is not superfluous if you have logic in your association model
  3. has_and_belongs_to_many is good enough for you

See example below

class Student
  has_and_belongs_to_many :courses
  has_many :teachers, through: :courses
end

class Teacher
  has_many :courses
  has_many :students, through: :courses
end

class Course
  has_and_belongs_to_many :students
  belongs_to  :teacher

  def boring?
    teacher.name == 'Boris Boring'
  end
end

In the example above, I make use of both versions. See how Course would have its own logic? See how a class for CourseStudent might not? That's what it all comes down to. Well, to me it is. I use has_many through for as long as I can't give a proper name to my association model and/or the model doesn't need extra logic or behavior.

Upvotes: 0

Related Questions