When I developed my first sandbox application, I wanted to get some records for relational table.
User.rb:
class User < ActiveRecord::Base
#has many followed articles
has_many :follow_articles
And FollowArticle model:
class FollowArticle < ActiveRecord::Base
belongs_to :user
belongs_to :article
end
And Article model:
class Item < ActiveRecord::Base
has_many :follow_articles
end
I want to get all followed articles of a user so in my controller I have:
#articles = current_user.follow_articles
which gave me:
ActiveRecord::Associations::CollectionProxy::ActiveRecord_Associations_CollectionProxy_FollowArticle:x3014X2
In my view I can iterate over these articles:
<% #articles.each do |article| %>
<%= article.article.name %>
<% end %>
which works perfectly.
Can I do this in this way to get an Articles array instead of a FollowArticles array, something like:
#items = current_user.follow_articles
to return articles instead of followArticles?
Use has_many :through.
From the Guide:
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
So, fully connecting the dots...
#User.rb
class User < ActiveRecord::Base
has_many :follow_articles
has_many :followed_articles, through: :follow_articles
end
Related
i'm new to both rails and web-dev.
currently i'm studding "active record Associations" in rails 4
and i got confused on usage of "has_many" vs "has_many, through" relation.
for example, if i have Physician, Appointment, and Patient model in my schema.(As rails guide provides)
and rails tutorial suggests me to do 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
but what if i make relation like this
class Physician < ApplicationRecord
has_many :appointments
end
class Appointment < ApplicationRecord
belongs_to :physician
has_many :patients
end
class Patient < ApplicationRecord
belongs_to :appointment
end
i think both will work fine.
but i want to know whats the differences and why they made "has_many, through" relations.
thank you for reading my question.
has_many through is a way to join two unrelated, independent models/tables and to reduce duplicates/redundancy in tables, where has_many states more directive relation.
Maybe example with appointments and physicians isn't clear. I'll give a different one.
class Artist
has_many :paintings
has_many :purchases
has_many :buyers, through: :purchases
end
class Painting
belongs_to :artist
end
class Purchase
belongs_to :painting
belongs_to :buyer
end
class Buyer
has_many :paintings_buyers
has_many :painting, through: :purchases
end
Talking about your example.
First, there is no convenient way for you to get physician's patents. The only way is:
physician.appoitments.map(&:patient).uniq
Which will result in
poor performance
inability to filter entities with sql, only with ruby(again poor performance)
Also, did you notice I used uniq? That's because patient's table will have lots of duplicates when the same patients will be appointed multiple times to the same physician.
I'm trying to query on ActiveRecord multiple nested eager loaded associations with conditions like so:
user.books.includes(slot: [room: :school]).where("books.slot.room.school.id = 1")
Obviously this query is wrong, but basically what I'm trying to reach is a relation of user.books for a certain school.
My model structure is:
class User < ActiveRecord::Base
has_many :books
has_many :slots, through: :books
has_many :rooms, through: :slots
has_many :schools
end
class Book < ActiveRecord::Base
belongs_to :user
belongs_to :slot
end
class Slot < ActiveRecord::Base
has_many :books
belongs_to :room
end
class Room < ActiveRecord::Base
belongs_to :school
has_many :slots
end
class School < ActiveRecord::Base
has_many :users
has_many :rooms
has_many :slots, through: :rooms
has_many :books, through: :slots
end
Any ideas? Thanks in advance.
includes eager loads association records, while joins does what you want to do.
This question has been asked here numerous times. Please make sure that you look and try to find a similar question here before you ask one.
So you want to change your code like this:
user.books.joins(slot: [room: :school]).where(schools: { id: 1 })
Here is my models:
class User < ActiveRecord::Base
has_many :products
has_many :comments
end
class Product < ActiveRecord::Base
belongs_to :user
has_many :comments
end
class Comment < ActiveRecord::Base
belongs_to :user
belongs_to :product
end
I need to get comment records from current user products only
How do I do that? thanks
If we move the relationships to use a has_many: comments, through: products you can probably get what you're after:
class User < ActiveRecord::Base
has_many :products
has_many :comments, through: products
end
class Product < ActiveRecord::Base
belongs_to :user
has_many :comments
end
class Comment < ActiveRecord::Base
belongs_to :user
belongs_to :product
end
Now you can do user.comments.
The rails docs are here, which say:
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.
I have three tables: illnesses, symptoms and a third table to map the relationship between the first two, called symptom_illness.
This third table has symptom_id, illness_id and its own id
I need a way to show, for example, all symptoms of a given "Common Cold" illness. In this example, "Common Cold" has an id of 1 and its symptoms have ids of 1 through 5.
This means that symptom_illness has 5 entries, where:
symptom_illness.illness_id = 1, symptom_id = 1
symptom_illness.illness_id = 1, symptom_id = 2
symptom_illness.illness_id = 1, symptom_id = 3
And so on. I need a way to display, in a single page, all the symptoms that have the same illness_id but I can't seem to find a way how to.
EDIT 1: My classes are related as such:
Symptom:
has_many :symptom_illness
has_many :illnesses, through: :symptom_illness
And similar for Illness.
Illness_symptom has belongs_to :symptom and belongs_to :illness
You have three models
class Symptom
has_many :symptom_illnesses
has_many :illnesses, through: :symptom_illnesses
end
class SymptomIllness
belongs_to :illness
belongs_to :symptom
end
class Illness
has_many :symptom_illnesses
has_many :symptoms, through: :symptom_illnesses
end
You can easily access al illness symptoms with something like that:
Illness.find(1).symptoms.each do |symptom|
# do something with this symptom
end
What do you want to show in the page?
If the symptom has a name attribute you can initialize a
#symptoms = Illness.find(1).symptoms
array in controlller and in the page you do something like
<% #symptomps.each do |symptom| %>
<% = symptom.name %>
<% end %>
You should use a has_many through: relationship.
class Illness < ActiveRecord::Base
has_many :symptom_illness
has_many :symptom, through: :symptom_illness
end
class SymptomIllness < ActiveRecord::Base
belongs_to :symptom
belongs_to :illness
end
class Symptom < ActiveRecord::Base
has_many :symptom_illness
has_many :illness, through: :symptom_illness
end
http://guides.rubyonrails.org/association_basics.html#the-has-many-through-association
Class Symptom < ActiveRecord::Base
belongs_to :symptom_illness
has_many :ilnesses, through: symptom_illness
end
class SymptomIllness < ActiveRecord::Base
belongs_to :symptom
belongs_to :illness
scope :ilness, ->(*i) {
where(ilness_id: i.flatten.compact.uniq
}
end
class Illness < ActiveRecord::Base
belongs_to :symptom_illness
has_many :symptoms, through: :symptom_illness
end
Ilness.find(1).symptoms
I have three models which look something like this:
Class User < ActiveRecord::Base
has_many :comments
end
Class Comment < ActiveRecord::Base
belongs_to :user
has_many :votes
end
Class Vote < ActiveRecord::Base
belongs_to :comment
end
Now I want to get all the votes associated with a user's comments like so:
#user.comments.votes
But this throws the error:
undefined method `votes' for #<ActiveRecord::Relation:0x3f6f8a0>
This seems like it should work, but I suspect ActiveRecord is coughing on the deeper has_many relationship. I've hacked together an SQL query that gets the desired results, but I suspect there's a cleaner way using purely ActiveRecord. Any tips?
You should use a has_many :through association
In your case it would be
Class User < ActiveRecord::Base
has_many :comments
has_many :votes, :through => :comments
end
Class Comment < ActiveRecord::Base
belongs_to :user
has_many :votes
end
Class Vote < ActiveRecord::Base
belongs_to :comment
end
And then simply get the votes with
#user.votes
Try this:
Vote.joins(comment: :user).where(users: {id: #user.id})