I followed them over and over again but couldn't get my code to work. I knew I had the basic structure setup correctly, since the examples are pretty straightforward, and the concept is not difficult. My has_and_belongs_to_many code originally looked like this:
class Soda < ActiveRecord::Base
has_and_belongs_to_many :distributors
end
class Distributor < ActiveRecord::Base
has_and_belongs_to_many :sodas
end
Of course there was also a many-to-many join table migration:
class DistributorsSodasJoinTable < ActiveRecord::Migration
def self.up
create_table :distributors_sodas, :id => false do |t|
t.column :soda_id, :int
t.column :distributor_id, :int
end
end
def self.down
drop_table :distributors_sodas
end
end
This works quite nicely:
>> Soda.find(1).distributors
=> []
Later found that I needed to add attributes in the join table to associate extra fields on the Distributors <-> Sodas relationship. has_and_belongs_to_many does not have a Rails way to access those extra fields in the join table. I've successfully done it through SQL, but much guilt and remorse lead me to finally learn has_many :through.
This was my best initial attempt:
class Soda < ActiveRecord::Base
has_many :distributors_sodas
has_many :distributors, :through =>; :distributors_sodas
end
class Distributor < ActiveRecord::Base
has_many :distributors_sodas
has_many :distributors, :through => :distributors_sodas
end
class DistributorsSodas < ActiveRecord::Base
belongs_to :soda
belongs_to :distributor
end
All goes well until I try to do a quick test:
>> Soda.find(1).distributors
NameError: uninitialized constant Soda::DistributorsSoda
...
from (irb):4
Umm... what? I never tried to instantiate an object of the type Soda::DistributorsSoda. Instead, I was simply trying to use the DistributorsSodas ActiveRecord object, right?
It turns out that has_many :through (apparently) can't handle using the join tables created by has_and_belongs_to_many. Its just a naming issue - has_many :through will work fine using a one-to-many join table like distributor_sodas (note the missing 's' on distributor). If you need a many-to-many join, you have to rename the table to fix the (pluralization?) problem. I deleted the DistributorsSodas model and created the Store model.
class Soda < ActiveRecord::Base
has_many :stores
has_many :distributors, :through => :stores
end
class Distributor < ActiveRecord::Base
has_many :stores
has_many :sodas, :through => :stores
end
class Store < ActiveRecord::Base
belongs_to :soda
belongs_to :distributor
end
This proved a much better result:
>> Soda.find(1).distributors
=> []
In the end, the association naming convention actually make more sense. I was bummed to have to change the table/model names though.
Please comment if you know how to create the association without changing the model name.
I had been searching for this for 2 days. I kept getting the "naming bug", uninitialized error on the join table. Thanks so much for posting. I did the rename, and was all set afterwards.
ReplyDeleteGlad to hear the post was helpful.
ReplyDeleteI just figured it out. You have to specify :class_name, and :foreign_key on the join :belongs_to join model, even when the naming seems "inflectable"(is that a word?).
ReplyDeleteGuys,
ReplyDeleteI have tripped over this myself, and I think I have a tenuous grasp on what's going on that may help. I think the original example above would work like this:
class Soda ; :distributor_sodas
end
class Distributor :distributor_sodas
end
class DistributorSoda < ActiveRecord::Base
belongs_to :soda
belongs_to :distributor
end
First, the class name: DistributorSoda is always singular. The part I find confusing is that you (might) expect the underlying join table, and therefore the name used for the has_many declaration to be "distributors_sodas", as it would in a HABTM relationship, but instead its "distributor_sodas". In other words, its treated as one word pluralized instead of two words split by the underscore, with each pluralized. This makes sense when you consider that many join tables that are full fledged models go by a better name, like "store" in your example, or "membership" to give another popular example. I think this also explains why specifying :class_name and :foreign_key will also work; its not helping Rails figure out the proper pluralizatio so much as it is bypassing the whole pluralization issue by naming the join table/class/foreign keys explicitly.
Play The Real Money Slot Machines - Trick-Taking Game - Trick-Taking
ReplyDeleteHow to septcasino.com Play. หารายได้เสริม Play https://octcasino.com/ The Real Money Slot https://febcasino.com/review/merit-casino/ Machine. If you are searching for a fun, exciting game to play online, we 토토 have you covered.