Use find to select attributes from joined tables - ruby-on-rails

I have three tables,
QuestionSets
has_many :questions
Question
has_many :answers
Answers
Now the Answers table has a column called "actual_answer"
Now I wanted to do a find where I can get all the Answers with actual_answers equal to a specific value and belongs to a specific question_set.
I have this right now:
#questionSet= QuestionSet.find(params[:id])
#answers = Answer.find(:all, :conditions => ["actual_answer=?", SOMEACTUALANSWER])
answers_i_need = []
#answers.each do |answer|
if answer.question.question_set_id == #questionSet.id
answers_i_need << answer
end
end
Is there a BETTER way of doing this, as I'm expecting thousand array results and making a loop might not be a perfect way of doing it.
Thanks!

Why not use a has_many ... :through relation? Link to: guides
This way you can have:
QuestionSet
has_many :questions
has_many :answers, through: :questions
Question
has_many :answers
belongs_to :question_set
Answer
belongs_to :question
Then in your controller you have:
#questionSet= QuestionSet.find(params[:id])
answers_i_need = #questionSet.answers.where('actual_answer = ?', your_actual_value)
Oh, and if you have more than a thousand results to query, you might consider having a look at this.

Related

has_many order by model and another table

I have a Question model.
every question can have many answers. And answers have status id which is a number.
So: I have:
class Question < ActiveRecord::Base
has_many :answers, :order =>'status_cat_id'
Now, answers are related to users, and I want the order of answers to be first by status_is and then by user name.
So, when I'll call: #question.answers I will get the answers ordered first by status and then by the user name.
Is it possible to achieve that through the model?
The answer is:
has_many :answers, :include => :user, :order =>'status_cat_id, users.name'

Difficulty sorting entries based on attributes of related models

I am having trouble implementing a feature that allows users to see questions (my model) sorted or filtered based on various attributes belonging to it (i.e. was the question answered? how many answered per question, etc), which would be based on the Question model's attributes, or that of attributes of related models to Question.
I have the following models:
class Question < ActiveRecord::Base
belongs_to :course
belongs_to :user
has_many :answers, inverse_of: :question
belongs_to :accepted_answer, class_name: :answer, foreign_key: :accepted_answer_id
default_scope order: 'questions.created_at DESC'
end
class Answer < ActiveRecord::Base
belongs_to :user
belongs_to :question, inverse_of: :answers
has_many :a_votes
default_scope order: 'answers.created_at DESC'
def accepted?
return false if new_record?
question.try( :accepted_answer_id ) == id
# an alternative is to use question.try( :accepted_answer ) == self
end
end
What I would be add are sorts or filters in the controller such as "See only answered questions", where only questions that has question.accepted_answer == true. What would be the best way to achieve this? And are there any guides that I should consult on ActiveRecord filtering/sorting that I could use for future reference?
Thanks!!
Addendum
I am displaying the questions as rendered _question.html.erb and calling it via the show function of question's parent, Group (so, each Group will has_many questions)
class CoursesController < ApplicationController
def show
#course = Course.find(params[:id])
# #questions = #course.questions.all this is the default selection
#questions = #course.questions.by_answer_count #maybe Im not calling the scope correctly (and sorry if I am making a noob mistake..)
end
#There are other methods not shown
end
I achieve this sort of thing by defining a scope on the parent model using joins and grouping.
For example, this scope would order the questions by number of answers (descending).
class Question
scope :by_answer_count, -> {
joins(:answers).reorder("count(answers.id) DESC").group(:id)
}
end
I hope that helps.

Find all associated objects by specific condition

class QuestionGroup < ActiveRecord::Base
has_many :questions
end
class Question < ActiveRecord::Base
belongs_to :question_group
has_many :question_answers
has_many :question_users_answers, :through => :question_answers, :source => :user_question_answers
def self.questions_without_answers(user_id)
select {|q| q.question_users_answers.where(:user_id=>user_id).empty?}
end
end
class QuestionAnswer < ActiveRecord::Base
belongs_to :question
has_many :user_question_answers
end
I need find all Questions if they have no user answers I did it by class method self.questions_without_answers(user_id)
But how can I find all QuestionGroups where present questions_without_answers and for particular user?
P.S: I need to find all unanswered questions and all groups that own these questions, can I do it by find or named-scope?
UPDATED:
def self.groups_without_answers(user_id)
questions_ids = Question.questions_without_answers(user_id).map {|q| q.id}
all(:conditions => "id in (select distinct question_group_id from questions where id in (#{questions_ids.join(',')}))")
end
But I think it is not good or maybe I wrong?
class QuestionGroup < ActiveRecord::Base
has_many :questions
def self.without_answers(user_id)
joins(%"inner join questions on question_groups.id = questions.question_group_id
inner join question_answers
on question_answers.question_id = questions.id
inner join question_groups
on question_answers.question_users_answers_id = question_users_answers.id").where("user_question_answers.user_id" => user_id).select { |qq| ... }
end
end
end
You can change some of the inner joins to left out join to pick up records where the table you are joining to doesn't have a match, for instance where there is no answer. The fields of the table you are joining to will have NULL values for all the fields. Adding a where id is null will even filter to just the questions with no answers.
Keep in mind that this is just an alternative technique. You could programmatically solve the problem simple with:
class QuestionGroup
def self.question_groups_without_answers(user_id)
select {|qq| qq.question_users_answers.where(:user_id=>user_id).empty?}.map{ |qq| qq.question_group }
end
end
An advantage of doing the joins is that the database does all the work, and you don't send several SQL queries to the database, so it can be much faster.

How do I sum a many to many value?

Each User can have many Resources, and each of those Resources has many Votes, and each of those votes have a value attribute that I want to sum all that particular users resources.
If I were to type this in a syntactically incorrect way I want something like...
#user.resources.votes.sum(&:value), but that obviously won't work.
I believe I need to use collect but I am not sure?
This is the closest I got but it prints them out, heh
<%= #user.resources.collect { |r| r.votes.sum(&:value) } %>
I'd recommend setting up a has_many :through relationship between the User and Vote objects. Set the models up like this:
class User < ActiveRecord::Base
has_many :resources
has_many :votes, :through => :resources
end
class Resource < ActiveRecord::Base
belongs_to :user
has_many :votes
end
class Vote < ActiveRecord::Base
belongs_to :resource
end
Once this is done you can simply call user.votes and do whatever you want with that collection.
For more info on has_many :through relations, see this guide: http://guides.rubyonrails.org/association_basics.html#the-has_many-through-association
How can you tell who voted having a Vote instance? Your Vote model has to have voter_id field and additional association:
# in Vote.rb
belongs_to :voter, class_name: 'User', foreign_key: 'voter_id'
And in your User model:
# in User.rb
has_may :submited_votes, class_name: 'Vote', foreign_key: 'voter_id'
So, #user.votes (as David Underwood proposed) will give you #user resources' votes. And #user.submited_votes will give you votes submitted by the #user.
Using just User <- Resource <- Vote relation won't allow you to separate some user's votes made by him and votes made for its resources.
For a total sum this should work or something real close.
sum = 0
#user.resources.each do |r|
r.votes.each do |v|
sum += v.value
end
end
This might work for you:
#user.resources.map {|r| r.votes.sum(:value)}.sum
How many records do you have, there is a way to push this to the database level I believe, I would have to check, but if it is only a few records then doing this in ruby would probably be ok
Try this code
#user.resources.map(&:votes).flatten.map(&:value).sum

Trying to get counts through multiple associations

I have created a question an answer app for a client much like StackOverflow. I am not trying to implement some sort of point system (like SO reputation). I am trying to get certain record counts through associations (which I believe are set up correctly). Primarily I am trying to get counts for votes on users answers. Here is an example.
In /views/questions/show page I list all the answers to that question by calling a partial _answer.html.erb. With each answer I pull in the answer.user information (username, email, etc.) by simply doing answer.user.username. I am wanting to display in a badge like format some total point calculations. So if User A answered Question A, next to User A's answer I want to display a total of all User A's answer votes.
I can successfully get a count for a users answers in /views/answers/_answer.html.erb by doing the following:
<%= answer.user.answers.count %>
but when I try to extend that syntax/association to get a count of votes on all User A's answers I get undefined method errors.
<%= answer.user.answers.votes.count %>
Is my set up fundamentally wrong here or am I missing something.
That is a bit confusing so let me know if you need more detail.
UPDATE:
Here are the associations:
Answers
class Answer < ActivRecord::Base
belongs_to :question
belongs_to :user
has_many :votes, :dependent => :destroy
end
Votes
class Vote < ActiveRecord::Base
belongs_to :answer
belongs_to :user
belongs_to :question
end
Users
class User < ActiveRecord::Base
has_many :questions, :dependent => :destroy
has_many :answers, :dependent => :destroy
has_many :votes, :through => :answers , :dependent => :destroy
end
<%= answer.user.answers.votes.count %>
but
answer.user.answers
is an array of answers so I suppose you wanted something like
<%= answer.user.answers[id].votes.count %>
UPDATE
<% answer.user.answers.each do |answer| %>
<%= answer.votes.count%>
<% end% >
UPDATE
<%= (answer.user.answers.map{|x| x.votes.count}).sum%>
I LOVE rails for such things
but when I try to extend that syntax/association to get a count of votes on all User A's answers I get undefined method errors.
You can find complete listing of what you can do with user.answers collection in here (besides standard Array/Enumerable methods). And it's only collection instance, not Answer object, so you can't invoke methods from Answer model on it.
You can try setupping has_many :votes, :through => :answers relationship. (See here for details) Although, I'm not sure if :through would work in such case.
Alternatively, you can create a method in User model to return all Vote objects (simply by iterating through all answers)
But frankly, creating a horde of Vote objects simply to count them sounds like a terrible waste of resources to me.

Resources