Search code examples
ruby-on-railsfixtureshas-and-belongs-to-manyfactories

Rails has_and_belongs_to_many is confusing me with fixtures and factories


General Confusion
I have bands which can have 3 genres. I read in a previous SO post that the proper way to handle this is a couple steps:

1) In band.rb

has_and_belongs_to_many :genres

2) Create a band_genres join table

Even after reading the documentation, I am a bit confused as to what HABTM actually means. I guess I would just normally think "a band has many genres", not has and belongs to many. So a quick DUMBED down explanation of that would be great.

Confusion with Fixtures

Also, when doing my fixture for band_genres I have

{ 
  "The Reaper Band and Funk": { "band": "The Reaper Band", "genre": "Funk" },
  "The Reaper Band and Rock": { "band": "The Reaper Band", "genre": "Rock" }  
}

And I get a "unknown" band column. I thought rails was supposed to know that "The Reaper Band" would refer to a band from a band fixture (same name of course) and would grab that id and know that "band" in this fixture would refer to band_id in the join table. I would rather my fixtures look like this than have hard coded numbers.

Confusion With Factories

When I create a band in my factory, I want to assign it genres:

Factory.define :band do |f|
  f.sequence(:name) { |n| "Band#{n}" }
  f.mailing_lists { |mailing_lists| [mailing_lists.association(:mailing_list)] }
  f.genres 2
end

I realize here I would probably need a hard coded genre_id. But why doesn't rails look at that and say "oh, he wants to add genre with id=2 to the band_genres table".

I am not expecting rails to take care of all the dirty work for me, but I do want to play by the rules.


Solution

    1. Has and belongs to many defines the relationship in both directions. If you only need to see what genres the band belongs to, has_many will be fine. If you want to know what bands are "funk", you can use HABTM to allow a lookup of bands for a given genre.
    2. For fixtures, in rails you can now do HABTM without creating a separate fixture for the join table. For example:

    in bands.yml:

    reaper_band:
      name: The Reaper Band
      genres: funk, rock
    

    in genres.yml:

    funk:
      name: Funk
      bands: reaper_band
    
    rock:
      name: Rock
      bands: reaper_band