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.
Related
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.
I did a lot of searches for this problem and watched quite a few tutorials (e.g railscast: nested model form) but I'm afraid it's not the right solution to my problem.
I want to collect data for a theory. This theory has many questions to be answered on distinctive cards. I always want to display all questions (on all cards), but the answers may vary, depending on each card. Therefor I need to catch the question_id and card_id, but deep nesting and catching the id by params would be quite a pain. Does anyone know a better solution for my problem?
Here are my models:
theory.rb
class Theory < ActiveRecord::Base
has_many :cards
has_many :questions
card.rb
class Card < ActiveRecord::Base
belongs_to :theory
has_many :answers
has_many :questions (Do I need this here - I always want to display all questions?)
question.rb
class Question < ActiveRecord::Base
belongs_to :theory
has_many :answers
answer.rb
class Answer < ActiveRecord::Base
belongs_to :card
belongs_to :question
Thank you a lot for your help!
The relationships between your models should not be determined by how they will be displayed on the screen. They should be determined by how they are actually related to each other.
I don't fully understand what theory/card are supposed to represent so I can't tell you what relationships need to exist, but I can have a stab at at it :
It seems to me as though a card represents the answers for a single person that relate a to a specific Theory? If so, then you don't need the "has_many :questions" because the card only has answers. Also, each answer links to a question so you can "get" to the questions through the answers. You could add a "has_many :questions, :through=>:answers" to the Card model if you really wanted the list of questions for a card.
If each card can have a different set of questions added to it before any answering is done, then you will need the "has_many :questions", but if all the questions are on each card then you won't need it.
Update in response to your comments below :
One way is to add the answers to the card by building them up in the controller:
#card = Card.find 2
#theory = Theory.find 1
#theory.questions.each do |question|
#card.answers.build :question=>question
end
Then in your view do something like this (this is HAML) :
= form_for #card do |f|
-#card.answers.each do |answer|
=f.fields_for answer do |answer_form|
=answer_form.hidden_field :question_id
%p=answer.question.full_question
%p=answer_form.text_field :input
This is using nested form attributes, so to get it to work with your Card model you'll have to add this to it :
class Card < ActiveRecord::Base
..
accepts_nested_attributes_for :answers
..
end
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
It's driving me crazy. I have 3 models. User, Photo, Comments.
Here is what I want to do.
A user has many photos and comments
A photo belongs to a user and has many comments
And a comment belongs to user and to a photo
So my models have these associations:
User
has_many :photos, :dependent => :destroy
has_many :comments, :dependent => :destroy
Photo
belongs_to :user
has_many :users, :through => :comments
has_many :comments, :dependent => :destroy
Comment
belongs_to :photo, :user
I want now to show a photo and load all the comments of this photo and display each comment with the user info.
So at the photos controller show action I have
#photo = Photo.find(params[:id], :include => :comments, :order => 'comments.created_at DESC')
And in the photo/show view
=render :partial => "/comments/partials/comment", :collection => #photo.comments, :as => :comment
It display the comments eg. the comment text fine but when inside the partial I try to do:
%p=comment.user.fname
%p=comment.body
It throws error "undefined method `fname' for nil:NilClass"
Something strange is that I am user authlogic so you have to be logged in to post a comment. But you can see the comments even if you are not logged in. When I am logged off it works find. When I log in it throws error.
Any help would be much appreciated cause it is driving me nuts.
By the way in my routes I have
map.resources :users, :has_many => [:photos, :comments]
map.resources :photos, :has_many => [:comments, :users]
Thanks
Not sure this will make a difference, but have you tried separating the belongs_to associations?
belongs_to :photo
belongs_to :user
+1 for using haml
i can think of a few reasons why this might happen
firstly, what happens when your remove the following line from your code?
# try removing this
%p=comment.user.fname
Does the error then move on to next variable (i.e. comment.body)
If not, then you have at least narrowed it down to the fname variable. in which case I would wonder if you have perhaps added the fname variable to your model after creating some intitial database entries... this would mean that some of hte entries don't have associated fname variables. in this instance you can fix the issue by scrubbing the database and remigrating from scratch
Also, do you have attr_accessible set for the fname variable in your model? chekc that you have this set for all the variables.
can you look inside your db and make sure that all entries have an fname variable set?
I realise that you want to get this working but if you can't, there's no shame in using the disqus.com plugin - it saves you db space, helps to attract more comments as lots of people already have profiles and gives you some nifty moderator features... on the downside, you lose branding, and you can't use any of your own rjs effects..
good luck
This code is taken from a previous question, but my question directly relates to it, so I've copied it here:
class User < ActiveRecord::Base
has_many :group_memberships
has_many :groups, :through => :group_memberships
end
class GroupMembership < ActiveRecord::Base
belongs_to :user
belongs_to :role
belongs_to :group
end
class Role < ActiveRecord::Base
has_many :group_memberships
end
class Group < ActiveRecord::Base
has_many :group_memberships
has_many :users, :through > :group_memberships
end
New Question Below
How you take the above code one step further and add and work with friends?
class User < ActiveRecord::Base
has_many :group_memberships
has_many :groups, :through => :group_memberships
has_many :friends # what comes here?
has_many :actions
end
In the code above I also added actions. Let's say that the system kept track of each user's actions on the site. How would you code this so that each action was unique and sorted with the most recent at the top for all the user's friends?
<% for action in #user.friends.actions %>
<%= action.whatever %>
<% end %>
The code above may not be valid, you could do something like what I have below, but then the actions wouldn't be sorted.
<% for friend in #user.friends %>
<% for action in friend.actions %>
<%= action.whatever %>
<% end %>
<% end %>
UPDATE
I guess the real issue here is how to define friends? Do I create a new model or join table that links users to other users? Ideally, I'd like to define friends through the common group memberships of other users, but I'm not sure how to go about defining that.
has_many :friends, :through => :group_memberships, :source => :user
But that doesn't work. Any ideas or best practice suggestions?
It's been a while since I've worked with Rails (think Rails 1.x), but I think you can do something like the following:
Action.find(:all, :conditions => ['user_id in (?)', #user.friends.map(&:id)], :order => 'actions.date DESC')
and then in your view:
<% #actions.each do |action| %>
<%= action.whatever -%>
<% end %>
To add friends you can use has_and_belongs_to_many or has_many :through. Here is example how to do it. You should self join users table.
To list recent actions, get them from db using :order => :created_at (or :updated_at). Than you can filter out actions that not belongs to users.
#actions = Action.all(:order => :created_at).select {|a| a.user.friends.include?(#user)}
Probably you can also write sql query to do this.
You could make a "friend" object that uses the "user" table and go from there or you could
define a friend as a partial entity (friend name and user_id reference) and pull the user object into the friend object.
I like this last model better because it allows you to represent "deleted" friends more easily. You would still have a copy of the friend name (even if it is a duplicate of the user name) and the ID to identify if the user is gone or not. Keeps the "friend" concept a little further away from the "user" concept and any security implications. I will argue it's appropriate de-normalization to duplicate the "name" field as it adds flexibility and "shadow" information about a user that used to exist.
My take on it, though. You would still have to eventually self-join back to the user table, but perhaps not as much. If you REALLY hate to de-normalize anything you can have a Person object that Friend and User both refer to for information. Personally, I think that is a nice theoretical construction, but not worth the complexity it would introduce into the bulk of your code (80% of it being talking about the user and 20% talking about the friend). I would make the user easy to talk about and the friend a little hard... not both. :)
Hope this helps. I enjoyed mentally toying with this one. :)