rails has_many vs has_many through? - ruby-on-rails

I've read about the relationship identifiers has_many and has_many through. What I can't seem to understand is the difference between them. For example, if I had 3 models, Doctors, Appointments and Patients
class Doctor< ActiveRecord::Base
has_many :appointments
has_many :patients, through: :appointments
end
class Appointment < ActiveRecord::Base
belongs_to :doctor
belongs_to :patient
end
class Patient < ActiveRecord::Base
has_many :appointments
has_many :doctors, through: :appointments
end
Couldn't I just say that Doctor has_many :patients and Patient has_many :doctors and they'd be related? What's the purpose of going through appointments to do this?
thanks

You are right. If you say a doctor has_many :patients and a patient has_many :doctors, then they will be related.
However, I think what this tutorial is getting at is many-to-many association.
If the doctor model and the patient model are related by has_many, then a doctor exclusively owns a patient and a patient owns a doctor. But often, this may not be the case. A doctor can have many patients, and those patients do not have to belong to the doctor exclusively; they might have other doctors.
That's when many-to-many association comes in. In a many-to-many association, an object can have many objects which belong to it but not exclusively. It's just like the association between the doctor model and the patient model.
There are two ways to create a many-to-many association:
has_and_belongs_to_many
has_many #something through: #joining table
In your case, you are using the second way, with the joining table assocation.
Check out this Railscast on detailed explanation of these two. Also, this this official Rails documentation on associations will be helpful.

The only reason to use a 'through' table is when you would like to use some relating data contained in the middle table, in this case the appointment data relating to both doctors and patients.
Also, has_many expects a related belongs_to and visa-versa, so you have to use has_and_belongs_to_many in both models to indicate a many-to-many relationship, and create the appropriate join table to go with it.
Otherwise, yes, you could simply use has_and_belongs_to_many :patients and has_and_belongs_to_many :doctors in their respective files.
Pay particular attention to section 2.8 in the Rails Guide. It may take a few read throughs, but once you get it, it will make sense, I promise.

Related

How to handle a three way associations in RoR?

I am making a school application where a list of courses is needed for both the lecturers and the students, a list of lecturers for the students and a list of students for the lecturers.
So I have three models: staff, student and course.
I am new to RoR and associations don't make the most sense to me right now, but I want each staff and student to have multiple courses. So I thought has_many and belongs_to. Then I want each student to have a list of his/her lecturers, so has_many :staff, through: :course and the same for staff has_many :student, through: :course. I also want to use a paticular column for the relation, say course_code not an id.
staff.rb
has_many :courses
has_many :students, through: :courses
student.rb
has_many :courses
has_many :staff, through: :courses
course.rb
belongs_to :staff
belongs_to :students
Would this be the best way to do it if I want to be able to do #student.course.all and #student.lecturer.all?
If you want both students and lecturers to have courses, you'll need to use a polymorphic association.
https://guides.rubyonrails.org/association_basics.html#polymorphic-associations
Here's a guide that tells you how to implement!
EDIT: You don't need to use a polymorphic association, but it'll help DRY out your code. In addition, use concerns!
EDIT: Also, I think you need a join table. (A course can have many students, and a student can have many courses).
So a student can follow many courses, staff can teach many courses, a courses is followed by 1 or more students, and it is teached by 1 or more staff. Reading this: you need a join table.
Still as usual, there are many different approaches to do this. Let me start with the simplest first: create two join-tables: followed_courses and lectured_courses.
Class FollowedCourse
belongs_to :student
belongs_to :course
Class LecturedCourse
belongs_to :lecturer
belongs_to :course
Class Course
has_many :followed_courses
has_many :students, through: :followed_courses
has_many :lectured_courses
has_many :lecturers, through: :lectured_courses
Class Lecturer
has_many :lectured_courses
has_many :courses, through: :lectured_courses
Class Student
has_many :followed_courses
has_many :courses, through: :followed_courses
("Staff" is a confusing word in ruby on rails, since it has no plural: how do you indicate a group op staff versus a single staff member, that's why I propose lecturer here, but of course you could use teacher or anything similar).
What I could imagine is that a person, sometimes has the role of student, and sometimes the role of a teacher, so you could have a different join-table, called ParticipatingCourses where it belongs_to a person and a course, and has a role to indicate in which way they participate: student or lecturer. This makes things a little harder, so I think for now it would be simpler to stay with my first proposal.

Belongs to many association?

I guess this is a pretty conceptual question. I'm looking through the possible associations available through Rails, but cannot seem to wrap my head around how to build a "belongs_to_many" and "has_many" association.
Specifically, I want readers to have many books, and each book to belong to many readers.
The closest I can find is the "has_many_and_belongs_to" association, but based on all of the examples I found, it is not exactly accurate.
Likewise, according to the documentation, the "belongs to" and "has many" association is meant as a one to many.
Is there an association available that matches a belongs to many style or some sort of model structure I could use?
Update: Jul 2022
has_and_belongs_to_many is not recommended anymore.
Please use has_many :through approach.
You need to use either
has_and_belongs_to_many
class Book < ActiveRecord::Base
has_and_belongs_to_many :readers
end
class Reader < ActiveRecord::Base
has_and_belongs_to_many :books
end
with this approach, you will need to create a join table named books_readers
rails g migration CreateJoinTableBooksReaders books readers
OR
has_many :through
class Book < ActiveRecord::Base
has_many :book_readers
has_many :readers, through: :book_readers
end
class Reader < ActiveRecord::Base
has_many :book_readers
has_many :books, through: :book_readers
end
class BookReader < ActiveRecord::Base
belongs_to :reader
belongs_to :book
end
with this approach, you will need to create a new model BookReader
rails g model BookReader book:references reader:references

Which assosciation to use in Rails?

I have a user and project model created in Rails. I have to perform an association that will create an relationship which is described below:
1 User has many Projects
1 project has many Users
How can I go about creating an association for the same in Rails? I need some help on which type of association in rails will help me to achieve this.
You are describing a many-to-many relationship type. Rails allows you to create this relationship using has_many :through and has_and_belongs_to_many directives. You can learn the difference here.
Shortly, has_many :through allows you to add additional columns into the intermediate table, has_and_belongs_to_many doesn't. If you don't need to have additional attributes in the intermediate table than use has_and_belongs_to_many syntax. You can always change to has_many :through later.
class Project < ActiveRecord::Base
has_and_belongs_to_many :users
end
class User < ActiveRecord::Base
has_and_belongs_to_many :projects
end
You are basically trying to have a many-to-many relationship.
In Rails you can do this based on two association concept:
has_and_belongs_to_many (HABTM)
has_many :through
Note:
You should have HABTM if you just do not care about the way these two tables are joined (relationship model) and you do not want to have any logic/validation for your join data. It will just keep your foreign keys in a table and based on that data will be fetched.
You need has_many :through if you want to have an intermediate model in between Project and User which can be called as UserProject model. This way your association could look like as follows:
User Model:
has_many :user_projects
has_many :projects, through: :user_projects
Project Model:
has_many :user_projects
has_many :users, through: :user_projects
UserProject Model:
belongs_to :user
belongs_to :project
You can use has_and_belongs_to_many or has_many through.Here is the link I am providing which will help you to sort out difference between them and which one will be good for you.Here is the video tutorial for you association.ALso there is a good link link.In your case you need has and belongs to many
The best thing to do in this situation is ,
In your user.rb model file:
has_and_belongs_to_many :projects
and In your project.rb model file:
has_and_belongs_to_many :users
You may want too use many to many relationship between project and user. on top of that you may want to visit rails official guide which describes all of these relations in great detail.
http://guides.rubyonrails.org/association_basics.html

Associating 3 Models in Rails

I'm a little confused on which type of association I should set up in my application.
I have these three models: Booking, Availability and Facility
A facility puts up 1 hour blocks of available time. So I'd say Availability: belongs_to :facility.
I'm trying to represent that a booking could have many availabilities. In that if I book from 1-3PM that's two 1 hour blocks and I'd like for that to be represented in 1 record in the Bookings table.
I was thinking I should set up Bookings: has_many :availability
but then I was reading about has_many though and I wasn't sure if it would be more appropriate to do Facilities has many Bookings through Availability...?
I would absolutely do a has_many :through association here, but I'd make the association between Availability and Booking slightly different than a typical has_many :through association:
class Facility < ActiveRecord::Base
has_many :availabilities
has_many :bookings, through: :availabilities
end
class Availability < ActiveRecord::Base
belongs_to :facility
has_one :booking
end
class Booking < ActiveRecord::Base
belongs_to :availability
end
The reason I prefer this association style is because in a typical has_many :through, you have two entities sharing relationships with one another through a third entity (e.g. Patients and Doctors sharing a relationship through an entity called Appointment, as explained by the Rails guides). This example is different, however, in that Booking shouldn't hold any positive relationship to Facility--the relationship should merely be a by-product of an open Availability.

Double entry table in rails

I would like to create a double entry table form according two models.
For now I'm able to create a simple table with the members of a communities
on the columns, I must add the informations of an other model, like this :
My models :
Community
has_many :memberships
Membership
belongs_to :user
belongs_to :community
User
has_many ::memberships
has_many :skills
Skill
belongs_to :user
belongs_to :community
I there some gem existing to make a double entry table or is it easier to make it from scratch? if so, how can I begin ?
It seems like you would benefit from a through relationship here.
Instead of referencing community directly from the skill table, you could do:
Skill
belongs_to :user
has_many :communities, :through => :user
On user, add:
has_many :communities, :through => :memberships
Wouldn't this get the link between skill and community that you would like?
As Jay mentioned, you would benefit from a has_many :through relationship, or maybe a has_and_belongs_to_many relationship; whether it's the actual solution we'll have to see:
#app/models/user.rb
Class user < ActiveRecord::Base
has_many :memberships
has_many :skill_users
has_many :skills, through: :skill_users
end
#app/models/skill_user.rb
Class SkillUser < ActiveRecord::Base
belongs_to :skill
belongs_to :user
end
#app/models/skill.rb
Class Skill < ActiveRecord::Base
has_many :skill_users
has_many :users, through: :skill_users
end
This will allow you to associate each user (note that members are different than users) with specific skills without using double-entries in your tables
Relational
The basis of what you're seeking can be found in Relational Databases
These work by storing data in single instances, and linking to other data through foreign_keys. These foreign keys are things such as user_id etc:
(more information here)
This means instead of populating the same data twice, it is correct to reference that data from other models, as required. This is where join models come in
Join Model
Join models allow you to "link" two pieces of data through a join model:
For you, it means storing your skills in its own model, and linking users with skills on a join model (I've called skill_user.rb). This means that you'll be able to call your user's skills like this:
#user.skills #-> goes through the join model

Resources