rails model association in nested associations - ruby-on-rails

Say I have two models, Question which has_many Answers and Answers which belongs_to question. If in my Users model I add has_many Questions, would Users then automatically has_many Answers as well, or would I need to add has_many Answers manually?

You should read up on the "has many through" relationship.
http://guides.rubyonrails.org/association_basics.html#the-has-many-through-association

Not automatically. For users to have many answers, you'd want to use :through, e.g.,
class User < ActiveRecord::Base
has_many :questions
has_many :answers, through: :questions
end
See: http://guides.rubyonrails.org/association_basics.html#the-has-many-through-association

If you have the following setup:
class User << ActiveRecord::Base
has_many :questions
end
class Question << ActiveRecord::Base
has_many :answers
end
class Answer << ActiveRecord::Base
belongs_to :question
end
You can't get to all user's answers with a single method. You would have to get all questions as a collection and then iterate through your questions collection and get all the answers. This would obviously lead to N+1 query problem.
#user.questions.map(&:answers).flatten
To solve that N+1 query problem, you'd have to use .includes(), but again that would only solve the N+1 problem.
#user.questions.includes(:answers)
Solution: use has_many :through
If you add has_many :through into your User model:
class User << ActiveRecord::Base
has_many :questions
has_many :answers, through: :questions
end
This would allow you to access your user's answers with a single method like this:
#user.answers

Related

How do I get all data associated with a nested model?

I have 3 models: Quiz, which has_many questions, which has_many answers. The lower models all belongs_to the model intuitively above them (quiz > questions > answers).
My problem is that I want to get a collection of all the answers. I thought I could do this with something like quiz.questions.answers or quiz.questions.all.answers but I just get an error undefined method 'answers'. Why is this happening and how do I fix it?
Answer belongs to one Question so you cannot call answers on a collection of questions, so if you want all the answers of all the questions of a quiz then you can do something like this to achieve, in Quiz model add this
has_many :answers, through: :questions
By adding that you will be able to fetch all the answers directly by doing quiz.answers.
Now, if you want to get answers of specific questions then you can do something like this
quiz = Quiz.where(id: quiz_id).include(:questions => :answers)
quiz.questions.each do |question|
answers = question.answers
# perform some action
end
Hope that helps!
This is the perfect example of something that needs a "through" association.
class Quiz
has_many :questions
has_many :answers, :through => :questions
Then:
my_quiz.answers
class Quiz < ActiveRecord::Base
has_many :questions
has_many :answers, through: :questions
end
class Question < ActiveRecord::Base
belongs_to :quiz
has_many :answers
end
class Answer < ActiveRecord::Base
belongs_to :question
end
Then you can have,
quiz = Quiz.last
quiz.questions
quiz.answers

Ruby on Rails has_many :through in a polymorphic association

I have searched and searched and found only partial solutions to my current question.
The thing is, I'd like to know if it is possible to use has_many :through along with a polymorphic association in Ruby on Rails.
I have a system where students can create travel plans (that can belong to many students) and refund claims (that can belong to only one student) for their projects. In this system, both admin users and students are able to comment on the plans and claims.
My associations are:
class Student < ActiveRecord::Base
has_and_belongs_to_many :travel_plans
has_many :refund_claims
has_many :comments, through: :travel_plans
has_many :comments, through: :refund_claims
end
class AdminUser < ActiveRecord::Base
has_many :comments
end
class TravelPlan < ActiveRecord::Base
has_and_belongs_to_many :students
has_many :comments, as: :commentable
end
class RefundClaim < ActiveRecord::Base
belongs_to :student
has_many :comments, as: :commentable
end
class Comment < ActiveRecord::Base
belongs_to :commentable, polymorphic: true
end
My questions are:
Is it correct to associate comments twice in the Student model?
I don't want the AdminUsers to have travel plans and refund claims, how can I identify their comments as being made on a travel plan or on a refund claim?
Would there be a better approach?
Thanks a lot in advance for everyone!
Cheers,
You probably want to add an polymorphic author attribute to the Comment model. Than you just need has_many :comments, as: :author to the Student and AdminUser model.
If this is a new application and you are starting on the green field you might want to rethink your models a bit and add a Role and a User model. Student would than be a role of user as would AdminUser be.
Is it correct to associate comments twice in the Student model?
No, not really. If you have duplicate association name, you can only use one of them. If you want to use both, you have to name them differently.

rails has_many through same destination

I have a User, Post and Like model. A user has many posts, and a user can like posts.
has_many :user_posts
has_many :users, through: :user_posts
has_many :likes
has_many :users, through: :likes
On the other side my rspecs are:
it { should have_many(:users).through(:user_posts) }
it { should have_many(:user_posts) }
it { should have_many(:likes) }
it { should have_many(:user_likes).through(:likes) }
This doesn't seem to go down very well, as rspec complains there already is a relationship through user:
Expected Post to have a has_many association called users (, Expected users to have users through likes, but got it through user_posts)
I tried all sort of combinations with class_name without much success. How should I define these relationships?
I'm making some inferences based on the code above. Let me explain the choices I made...
Your terminology around "user posts" is ambiguous to me, because it's unclear if a "user post" is something a user liked or something a user authored.
In my code, has_many :posts is referring to authorship and does not demand a join table because a post only has one author. On the other hand, has_many :liked_posts needs to go through: a join table (Likes) since it's a many-to-many relationship.
The only other thing that I've done differently is the use of the source: option in the association. Since a Like has no idea what either liked_posts or users_who_liked are, you need to tell it which of it's own associations to follow to get the records.
You can read more about Associations in the Rails API Documentation.
class User < ActiveRecord:Base
has_many :posts
has_many :likes
has_many :liked_posts, through: :likes, source: :post
end
class Like < ActiveRecord:Base
belongs_to :user
belongs_to: post
end
class Post < ActiveRecord:Base
has_many :likes
has_many :users_who_liked, through: likes, source: :user
end
Finally, as feedback on your question as-written: Tom's right in his comment above. It's not clear which classes those associations are a part of. Does a User have Users twice? Confusing. If I mis-inferred any of your business rules, let me know and I can revise my thoughts.

Saving and retrieving state in web forms

I'm creating a 'work book' ruby on rails application. Basically a user will read a paragraph or two and then answer some questions based on what they've read.
I'm unsure how to model this. The paper work books have many pages and each page has many paragraphs.
Should I model it exactly as it is in physical form or should I simplify the model?
My biggest question is how do I save state so that a user can update their workbook and or make corrections after they've submitted part of their assignment?
I think these relations could work for you:
class WorkBook
has_many :pages
class Page
belongs_to :work_book
has_many :paragraphs
class Paragraph
belongs_to :page
has_many :questions
class Question
belongs_to :paragraph
has_many :answers
class Answer
belongs_to :question
belongs_to :user
class User
has_many :answers
has_many :answered_questions, class_name: 'Question', through: :answers
If you want a better control on your answers (like value_string, value_float, value_boolean, etc.) I recommend you to add an extra model "AnswerValue".
You might feel like there is too much models in this case, but trust me, it makes your app flexible and allows you to add more "granular" features on each model / relation.
An extenstion with these relations: You could add a model "AnswerCorrection" which would act like an Answer but would be a way to save another value after the user update it's workbook.
Hope this helps!
I'd go for something like this:
class PaperWork < ActiveRecord::Base
has_many :pages
has_many :paragraphs, through: :pages
end
class Page < ActiveRecord::Base
belongs_to :paper_work
has_many :paragraphs
end
class Paragraph
belongs_to :page
belongs_to :paper_work, through: :page
end
Then you can model assignments somewhat around this idea:
class Asignment < ActiveRecord::Base
belongs_to :user
belongs_to :paragraph
# attributes: description, status(pending, completed, etc.)
end

Rails - track user's "actions" with relationship models

I'm building a math exercise app. I want to store questions that are separated into exercises and each exercise belongs to a different section. Also there are multiple sections for each grade (12 grades in total).
How do i track a user's answer (action)? For instance, when a user browses the app, decides to answer question 7 from exercise 'G1' in section 'G' of Grade 'Pre-K' and provides a correct answer i want to track that and in the future provide him with statistics about his performance.
Currently there isn't a connection between my User model to the questions and i wanted to hear from others as to what would be the most efficient way to tackle this (maybe the models right now are completely wrong)
This is the model diagram for now with has_many, belongs_to relationship (i'll add 'the_question':string and 'answer':string later on to the question model):
I think all that is missing is a Answer model, that is connected to the User.
Optionally, you can define the relations on the "parents" of Question too.
class Grade < ActiveRecord::Base
has_many :users, :through => :sections # optional. i.e. 'users that have answers in this grade'
end
class Section < ActiveRecord::Base
has_many :users, :through => :exercises # optional
end
class Exercise < ActiveRecord::Base
has_many :users, :through => :questions # optional
end
class Question < ActiveRecord::Base
has_many :answers
has_many :users, :through => :answers # optional
end
class Answer < ActiveRecord::Base
belongs_to :question
belongs_to :user
end
class User < ActiveRecord::Base
has_many :answers
end
Update: re-reading your description, I think CorrectAnswer would be better suitable than Answer.

Resources