Let’s say I have a table of products and a table of accessories Each accessory can have many products that it’s compatible with while each product has many accessories available for it It In the problem I’m trying to solve accessories cannot belong to products and products cannot belong to accessories So, unless I was to create a third table with fields for product_id and accessory_id, how could I allow each to have many of the other Do I have to create the third table? Thanks in advance
This is a basic part of what is called a many-to-many relationship, which is what you describe here. These relationships always rely on a third table in any relational database-backed application (i.e. this is not specific to Rails).
There are two ways to model this relationship in Rails. The first (and oldest) is the has_and_belongs_to_many relationship, which uses a simple table consisting of two columns (accessory_id and product_id), and does a "dumb" join between the two models. Nothing (besides the simple fact of bi-directional relation) is stored in the middle table.
The second approach, which has been recommended in preference to the "dumb join" for many years, is the has_many through relationship. That also uses a third table, but that table backs a full ActiveRecord model. It would have an id, along with the product_id and accessory_id, and could include as many other columns as you may need in order to properly model the relationship between the other two objects. For example, you could "decorate" the join between the two main objects with more specific details about their relationship.
Say if you had a Person, Team, and Membership model, you could use the Membership to carry the date the person joined the team, or their role on the team. Your models would look like this:
class Person < ActiveModel has_many :memberships has_many :teams, through: :memberships end
class Team < ActiveModel has_many :memberships has_many :people, through: :memberships end
class Membership < ActiveModel belongs_to :person belongs_to :team end
If you have any thoughts at all that you may want to have a richer connection between products and accessories, then by all means use the has_many through relationship.
Walter
Thanks Walter
You don't need to do that -- Rails cleans up those values for you when it saves the parent. So if you have
@user = User.new(user_params) @widgets = @user.widgets.build(widget_params)
When you save @user, the @widgets will be saved as well, and the correct ID will be put into each of the @widgets.
Walter
i was actually able to get @person.id after if @person.save Relationship.find_or_create_by(person_id: @person.id …
So if I’m using the second approach the has_many_through then how do i create a new record?
Start by reading the rails guide on active record relations, that shows you how to do that sort of thing.
Colin
This code @person.pictures << @picture
causes an error
Could not find the association :people_pictures in model Person
class PeoplePictures < ApplicationRecord
belongs_to :person
belongs_to :picture
class Person < ApplicationRecord
has_many :pictures, through: :people_pictures
class Picture < ApplicationRecord
has_many :people, through: :people_pictures
So if I'm using the second approach the has_many_through then how do i create a new record?
Start by reading the rails guide on active record relations, that shows you how to do that sort of thing.
Colin
This code `@person.pictures << @picture` causes an error
Could not find the association :people_pictures in model Person
class PeoplePictures < ApplicationRecord belongs_to :person belongs_to :picture
class Person < ApplicationRecord has_many :pictures, through: :people_pictures
class Picture < ApplicationRecord has_many :people, through: :people_pictures
If you have read the guide, as Colin recommended, really read every line of it, you would have seen this:
class Physician < ApplicationRecord has_many :appointments has_many :patients, through: :appointments end
class Appointment < ApplicationRecord belongs_to :physician belongs_to :patient end
class Patient < ApplicationRecord has_many :appointments has_many :physicians, through: :appointments end
See how each has_many is described with two, separate has_many relationships? One goes to the "join object" and another to the "joined object". You are missing the association to the 'join object', which in your relationship would be:
has_many :people_pictures
Walter
Yikes Actually was falling asleep messin with it last night Sorry
Naming conventions for the join table are the same as for has_and_belongs_to_many ?
Let's say I have a table of products and a table of accessories Each accessory can have many products that it's compatible with while each product has many accessories available for it It In the problem I'm trying to solve accessories cannot belong to products and products cannot belong to accessories So, unless I was to create a third table with fields for product_id and accessory_id, how could I allow each to have many of the other Do I have to create the third table? Thanks in advance
Naming conventions for the join table are the same as for has_and_belongs_to_many ?
There are no requirements on this. The join table should be named for the join model, not the sides of the relationship. The reason that the HABTM naming convention exists at all is because there is no model in the middle, just the table. That means that the table name must be inferred from the sides of the relationship, since there would be no other way to configure that.
Often in this area, you will see a join table name that means something specific to the relationship. So between User and Group, you might see Membership, and that might mean that User has:
has_many :memberships has_many :groups, through: :memberships
and Group has:
has_many :memberships has_many :users, through: :memberships
But you may want to say @group.members because it expresses the semantics of the relationship better:
has_many :members, through: :memberships, class_name: 'User', foreign_key: :user_id
This sort of relationship is endlessly configurable and flexible, which is another reason why it is preferred over HABTM.
Walter
@person.pictures << @picture tries to, create a new picture, not add it to the person_picture association table
Where does @picture come from? Did you build it in memory, or load it from the database? Rather than adding an object you created to an association, try building the associated object from the beginning, like this:
@picture = @person.pictures.build(picture_attributes)
Now that picture is automatically in the @person.pictures association, and has whatever other attributes you assigned to it.
When you save the picture, the corresponding person_picture instance will be persisted as well. The picture has to be created and saved before the person_picture record can be, otherwise where would the person_picture.picture_id value come from?
Walter
to do that i added accepts_nested_attributes_for :picture it didn’t create the association
This is a basic part of what is called a many-to-many relationship, which is what you describe here. These relationships always rely on a third table in any relational database-backed application (i.e. this is not specific to Rails).
There are two ways to model this relationship in Rails. The first (and oldest) is the has_and_belongs_to_many relationship, which uses a simple table consisting of two columns (accessory_id and product_id), and does a “dumb” join between the two models. Nothing (besides the simple fact of bi-directional relation) is stored in the middle table.
The second approach, which has been recommended in preference to the “dumb join” for many years, is the has_many through relationship. That also uses a third table, but that table backs a full ActiveRecord model. It would have an id, along with the product_id and accessory_id, and could include as many other columns as you may need in order to properly model the relationship between the other two objects. For example, you could “decorate” the join between the two main objects with more specific details about their relationship.
Say if you had a Person, Team, and Membership model, you could use the Membership to carry the date the person joined the team, or their role on the team. Your models would look like this:
class Person < ActiveModel
has_many :memberships
has_many :teams, through: :memberships
end
class Team < ActiveModel
has_many :memberships
has_many :people, through: :memberships
end
class Membership < ActiveModel
belongs_to :person
belongs_to :team
end
If you have any thoughts at all that you may want to have a richer connection between products and accessories, then by all means use the has_many through relationship.
Walter
Let’s say I have a table of products and a table of accessories Each accessory can have many products that it’s compatible with while each product has many accessories available for it It In the problem I’m trying to solve accessories cannot belong to products and products cannot belong to accessories So, unless I was to create a third table with fields for product_id and accessory_id, how could I allow each to have many of the other Do I have to create the third table? Thanks in advance
– You received this message because you are subscribed to the Google Groups “Ruby on Rails: Talk” group.
To unsubscribe from this group and stop receiving emails from it, send an email to rubyonrails-ta...@googlegroups.com.
To post to this group, send email to rubyonra...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/rubyonrails-talk/e161bc52-f089-493a-8d12-00f597726217%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
I was thinking of another use for has_many_through If I wanna put a new abstraction in between two existing relationships like adding a Blog model and letting users have many posts through blogs instead of users having many posts and all the layers under posts i can leave alone i don’t have to recode them
Can we revisit this one I’m doing a family tree and trying to manage spousal relationships so i have person has_many people through: :partnerships But now partnerships can’t have person_id twice so this is perplexing me
Google for
rails self referential has_many through
Colin
Surely if every person object(eg.father) has a unique Id. Then that Id can be included in their childrens identity and fathers Id can be put into his brothers identity and sister identity and parents identity.
Then it becames a searching for matching Id’s both depth wise and breadth wise.
Isn’t that what a family tree is all about creating relationships and you build it.
Cheers Dave