Rails model has_many :through relationship - ruby-on-rails

User, Car, Part, Tire.
User has many Cars. Car has many Parts. Part has many Tires.
In user.rb, I have
has_many :tires, through: :cars
However, it seems like this is not a correct way of doing it since there is another model Part in the middle.
How should I successfully do to make has_many relationships between User and Tire?

You just need to add the connection in the middle.
# User.rb
has_many :cars
has_many :parts, through: :cars
has_many :tires, through: :parts

Related

Is has_many still necessary when has_many through exists?

I have what I feel like is a super simple question, but I can't find an answer anywhere!
Question:
If I previously had a has_many relationship like this: has_many :wikis, do I keep this relationship if later on I create a has_many through relationship like the following?
has_many :collaborators
has_many :wikis, through: :collaborators
This is all in my User model.
Background:
In my rails app, I have a User model and a Wiki model. I just gave users the ability to collaborate on private wikis so I migrated a Collaborator model and then came the step to create the has_many through relationships. I wasn't sure if I still needed has_many :wikis after putting has_many :wikis, through: :collaborators.
The reason I am confused is because Users should still be able to create wikis without collaborators and I'm not sure how the has_many through relationship works under the hood.
Originally I had only User and Wiki with a one-to-many relationship.
# User model
class User < ApplicationRecord
...
has_many :wikis # should I delete this?
has_many :collaborators
has_many :wikis, through: :collaborators
...
end
# Collaborator model
class Collaborator < ApplicationRecord
belongs_to :user
belongs_to :wiki
end
# Wiki model
class Wiki < ApplicationRecord
belongs_to :user
has_many :collaborators, dependent: :destroy
has_many :users, through: :collaborators
...
end
Is has_many still necessary when has_many through exists?
has_many not necessary when presence has_many through like your model
has_many :wikis # should I delete this?
has_many :collaborators
has_many :wikis, through: :collaborators
should I delete this?
Yes, you can delete this one, you don't need this as the same belongs_to
From The has_many Association
A has_many association indicates a one-to-many connection with another model. You'll often find this association on the "other side" of a belongs_to association. This association indicates that each instance of the model has zero or more instances of another model. For example, in an application containing authors and books, the author model could be declared like this:
From The has_many :through Association:
A has_many :through association is often used to set up a many-to-many connection with another model. This association indicates that the declaring model can be matched with zero or more instances of another model by proceeding through a third model. For example, consider a medical practice where patients make appointments to see physicians. The relevant association declarations could look like 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
You can work with only has_many association without has_many :through, but this is one-to-many, this not many-to-many
The has_many Association (without has_many :through) is one-to-many connection with another model
The has_many :through Association is up a many-to-many connection with another model
Update
Look, one physician may have many patients, on the other hand, one patient may have many physicians if you use has_many association without through for patient then this called one-to-many association, that means one physician has many patients, on the other hand, one patient belongs to one physician, and now association looks like this
class Physician < ApplicationRecord
has_many :patients
end
class Patient < ApplicationRecord
belongs_to :physician
end
Update 2
The has_many through the standard format your models after edited
# User model
class User < ApplicationRecord
...
has_many :collaborators
has_many :wikis, through: :collaborators
...
end
# Collaborator model
class Collaborator < ApplicationRecord
belongs_to :user
belongs_to :wiki
end
# Wiki model
class Wiki < ApplicationRecord
has_many :collaborators, dependent: :destroy
has_many :users, through: :collaborators
...
end

Rails 4 has_many through naming

I'm having problems with a Rails 4 join table. I have quite a simple setup which is working elsewhere in my application using a non-conventionally named table for users, groups and usergroupmemberships. I'm trying to set it up this time using the proper conventional naming and it's just not working.
Models involved are User, ManagementGroup and ManagementGroupsUser
db tables: management_groups_user, management_groups, users
app/models/user.rb
Class User < ActiveRecord::Base
...
has_many :management_groups, through: management_groups_users
has_many :management_groups_users
....
app/models/management_group.rb
class ManagementGroup < ActiveRecord::Base
has_many :users, through: :management_groups_users
has_many :management_groups_users
app/models/management_groups_user.rb
class ManagementGroupsUser < ActiveRecord::Base
belongs_to :users
belongs_to :management_groups
The association appears to work from with #user.management_groups_users but nothing else. I'm fairly sure this is a problem with naming / plurality but I can't figure it out.
This is the model which joins the remaining models user.rb and management_group
#app/models/management_groups_user.rb
belongs_to :user
belongs_to :management_group
Since we are going to use model above to access another model management_group then
#app/models/user.rb
has_many :user_management_groups #This should come first
has_many :management_groups, through: user_management_groups
Since we are going to use model above to access another user model then
app/models/management_group.rb
has_many :user_management_groups
has_many :users, through: :user_management_groups
Now should work
Do it this way.
app/models/user.rb
has_many :user_management_groups
has_many :management_groups, through: user_management_groups
app/models/management_group.rb
has_many :user_management_groups
has_many :users, through: :user_management_groups
app/models/management_groups_user.rb
belongs_to :user
belongs_to :management_group
I hope these associations will help you.
This is another way if you pass foreign key and class name.
app/models/user.rb
has_many :user_management_groups, :foreign_key => "key", :class_name => "ClassName"
has_many :management_groups, through: user_management_groups, :foreign_key => "key", :class_name => "ClassName"
app/models/management_group.rb
has_many :user_management_groups
has_many :users, through: :user_management_groups
app/models/management_groups_user.rb
belongs_to :user, class_name: "ClassName"
belongs_to :management_group, class_name: "ClassName"
This is another way around.
It's important to realize there is a convention rails uses for HABTM and has many through. HABTM does not have a model, so it needs to infer the table name which, as others point out, is both plural and alphabetical order.
If you are doing has many through and have a model, the convention is that it wants singular first word, plural second. See examples
User has and belongs to many groups.
HABTM: table name should be groups_users
Has Many Through: table name should be user_groups (flip order is more intuitive)
Model for the latter would be UserGroup. Has many through would specify it as through: :user_groups

Does Rails have a built-in way to create triple-join objects? Do I create AR models for them?

My database stores data about a TV show. I want to store information about who worked on what episodes, and in what role.
Each crew member works on many episodes; each episode has many crew members; and crew members can work on the same episode in different roles (they can be writer and director on the same episode, for example).
I'm new to thinking about ActiveRecord, so I'm a bit confused about how to properly represent these relationships in Rails. I would really love it if someone could tip me off as to the 'Rails way' to do this.
My instinct is to create a triple-join (or double-join; three fields, anyway) table. I have three models: CrewMember (has a name), Episode (has a title, airdate, etc), and Role (has a job title, like Director, Writer or Actor). I could create a table with three fields: CrewMember_ID, Episode_ID, and Role_ID. Each row would then describe someone working on a specific episode in a specific capacity.
So the relationship would appear to be:
CrewMember has_many :episodes
Episode has_many :crew_members
CrewMember has_many :roles
Role has_many :crew_members.
Here's where I get confused. Do I create a new model for that table? And if I do, how exactly do I create instances of that object? I understand relationships like Author has_many :books where I can create a book with Author.books.new, but this kind of has-many has-many has-many thing stumps me a bit.
I can do what I want with SQL, I'm just having trouble approaching it from an ORM/ActiveRecord angle.
you can work around this issue by adding a model that collects the three models
rails g model big_model model1:references model2:references model3:references
Using "Jobs" as GreenTriangle suggested
class CrewMember < ActiveRecord::Base
has_many :jobs, dependent: :destroy, inverse_of: :crew_member
has_many :episodes, through: :jobs
has_many :roles, through: :jobs
end
class Episode < ActiveRecord::Base
has_many :jobs, dependent: :destroy, invserse_of: :episode
has_many :crew_members, through: :jobs
has_many :roles, through: :jobs
end
class Role < ActiveRecord::Base
has_many :jobs, dependent: :destroy, inverse_of: :role
end
class Job < ActiveRecord::Base
belongs_to :episode
belongs_to :role
belongs_to :crew_member
validates_presence_of :episode, :role, :crew_member
end

Rails associations between clients and sellers - has_many :through or has_and_belongs_to_many

I'm beginning a project with rails where there are products, clients and sellers. Each seller has_many products. Each Client has_many products. (And in my case, each client only buys one product at a time).
I want to know who are my clients' seller and my Seller's clients, knowing that, they'll be linked by, the purchase, of one product.
Should I use a has_and_belongs_to_many association between clients and sellers ? Or a double has_many through :products, like :
Seller :
has_many :clients through :products
Belongs_to :products
Client :
has_many :sellers through :products
Belongs_to :products
In order to avoid two belongs_to in the product class, could this work ?
class Client < ActiveRecord::Base
has_many :products, as: :productable
has_many :sellers, through: :products
end
class Seller < ActiveRecord::Base
has_many :products, as: :productable
has_many :clients, through: :products
end
class Product < ActiveRecord::Base
belongs_to :productable, polymorphic: true
end
Thanks in advance for your answer.
I would go with has_many :through here.
class Client < ActiveRecord::Base
has_many :products
has_many :sellers, through: :products
end
class Seller < ActiveRecord::Base
has_many :prodcuts
has_many :clients, through: :products
end
class Product < ActiveRecord::Base
belongs_to :client
belongs_to :seller
end
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.
And also see these Guides for choosing between HABTM and a has_many :through
I want to approach your question from the other end: let us start from the product. I think this will clarify a lot of things.
So you have three models: Seller, Client and Product.
A Product has a seller and client. In your model that would like this:
class Product
belongs_to :seller
belongs_to :client
end
This means that in the products table we have a column seller_id and client_id.
Afaik a product needs to have both, always. So this also means you cannot use a polymorphic association here. At least not the way you proposed it. If you write
belongs_to :productable, polymorphic: true
you will add the fields productable_id and productable_typeto yourProduct` model. But that is only 1 link (so either a seller or a client, but never both). You could introduce a link table here, so a product could be linked to many "productables" but in your case i think it is besides the point. You know a product has one seller and one client.
Secondly, now this is established, your Product is exactly the link-table between clients and sellers. So you do not have to introduce a new link-table, just use the one already there.
class Seller
has_many :products
has_many :clients, through: :products
end
class Client
has_many :products
has_many :sellers, through: :products
end
So in conclusion:
use the has_many :through because you already have the link table as a model. Only use a habtm if you do not care about the join-table (link-table).
you can't use a polymorphic association here, as you need two links (without introducing a link-table, which seems overkill imho). I like the explicitness, clarity, readability of having an explicit seller_id and client_id, and it is also easier to manage.

How to write has_many through two objects (to the same 'destination' object)?

What is the correct way to describe the relationship between a User and the Outcomes of their Questions and Contacts? I want to be able to call User.outcomes and get all outcomes for the user, regardless of whether the outcome was for a question or a contact.
Here are my models as they stand right now. Are the has_many through relationships described correctly?
User Model
has_many :questions
has_many :contacts
has_many :outcomes, through: :questions
has_many :outcomes, through: :contacts
Question Model
has_many :outcomes
Contact Model
has_many :outcomes
Outcomes Model
belongs_to :question
belongs_to :contact
So, this is probably not the ideal solution because it returns an Array instead of an ActiveRecord::Relation. That means you lose lazy loading and the ability to further add scopes and where statements and whatnot. It's better than writing SQL and should do what you want though:
class User < ActiveRecord::Base
has_many :questions
has_many :contacts
has_many :questions_outcomes, :through => :questions, :class_name => "Outcomes"
has_many :contacts_outcomes, :through => :contacts, :class_name => "Outcomes"
def outcomes
return questions_outcomes + contacts_outcomes
end
end
Please let us know if you come up with something nicer.

Resources