how is this rails call getting an object from just an id? - ruby-on-rails

i have a User model and a UserMessage model (a model for holding the private messages between two users)
in my view i have..
<% if #message_items.any? %>
<ol class="messages">
<%= render partial: 'message_item', collection: #message_items%>
</ol>
<%= will_paginate #message_items %>
<% end %>
which i render with...
<li id="<%= message_item.id %>">
<span class="user">
<%= link_to message_item.user.name, message_item.user %>
</span>
<span>
<%= message_item.title %>
</span>
<span>
<%= message_item.body %>
</span>
</li>
how is the object UserMessage(which is coming from message_item) able to render the User object? my design for the UserMessage just has the following attributes "id, user_id, from_id, title, body, created_at, updated_at".
i guess its from the user_id, and rails somehow makes the connection and is able to find the User object from the user_id. is that correct?
but i what i really want though, is the user from the from_id (the person sending the message). is there a way to retrieve that? i know doing something like.. message_item.user.from_id does not work.
the only way i could think of that works is by doing
<%= User.find(id= message_item.from_id).name %>
but that doesn't seem right putting so much code in my view. sorry but ive been super stuck. help would be much appreciated. thanks

You need the following models
class User < ActiveRecord::Base
has_many :received_user_messages,
:class_name => "UserMessage", :foreign_key => :receiver_id
has_many :sent_user_messages, :class_name => "UserMessage",
:class_name => "UserMessage", :foreign_key => :sender_id
end
class UserMessage < ActiveRecord::Base
# should have sender_id and receiver_id columns
# make sure you index these columns
belongs_to :sender, :class_name => "User"
belongs_to :receiver, :class_name => "User"
end
Do the following, to list the messages received by an an user:
current_user.received_messages(:include => :sender).each do |message|
p "[#{message.sender.name}]: #{message.content}"
end
Do the following, to list the messages sent by an an user:
current_user.sent_messages(:include => :receiver).each do |message|
p "[#{message.receiver.name}]: #{message.content}"
end

i think you are looking for foreign_key option for belongs_to in model. So what you need is to specify something like sender/from relation with from_id in UserMessage message
belongs_to :sender, foreign_key: :from_id, class_name: "User"
Then in template you just call this relation in view.
message_item.sender
It should work same as message_item.user.
For further reference visit documentation for associations
In addition i recommend you not call .name method in template, but specify to_s method in your model. Good approach pointed by klump in his answer is to use .include method for better performance. It will load user data while loading UserMessage data, not in another query.
Article.find :all, :include => :revisions
Code was derived from another answer — Rails ActiveRecord: Is a combined :include and :conditions query possible?

Try this:
In your UserMessage class, make two class methods named sender and recipient that perform the complex queries you want. Then use these methods in your view.

First you need to set the associations. I guess that one user has many messages and one message belongs to one user.
add to the models:
app/models/user.rb
has_many :user_messages
*app/models/user_messages.rb*
belongs_to :user
You might need to add a column to your user_messages table, called user_id
When you fetch the messages in the controller, tell rails to load the associated user right away, so rails doesnt have to do this later on:
#message_items = UserMessage.includes( :user ).all
Now you can access the user object "owning" the message really easy:
<%= message_item.user.name %>
If you need all the messages owned by a user this also is easy now:
#user = User.find( some_id )
#user.messages # this returns an array with all the message objects

Related

undefined method `concert_id' for #<FollowUp::ActiveRecord_Associations_CollectionProxy:0x00007f8d481b3dc0> Did you mean? concat

I have a model of follow_ups and volunteers as:
class FollowUp < ApplicationRecord
belongs_to :volunteer
belongs_to :member
belongs_to :concert
end
class Volunteer < ApplicationRecord
enum type: [:admin, :regular]
has_many :follow_ups, dependent: :delete_all
has_many :members, through: :follow_ups
end
Now I wanted to print follow_ups by all volunteers.
It was working fine when I tried in rails console i.e Volunteer.first.follow_ups
I want to show those value in the form, what I tried is:
Volunteer.all.each do |volunteer|
volunteer.follow_ups.concert_id
end
The has_many relation denotes a one-to-many association. Instead of returning a single object, it returns a collection of follow_ups.
That said, you can't do volunteer.follow_ups.concert_id, because follow_ups is an Active Record collection. Make an iteration instead:
volunteer.follow_ups.each { |follow_up| puts follow_up.concert_id }
The Ruby on Rails documentation has great content about Active Record Associations.
To collect such information you should use:
volunteer.follow_ups.pluck(:concert_id)
Edit:
It's very important to note that using pluck is more efficient than using iterators like map and each due to saving server RAM and request time. Then you can print to rails logger:
volunteer.follow_ups.pluck(:concert_id).each{|ci| Rails.logger.info ci}
Edit2
Referring to your text
I want to show those value in the form
If I understand you, you want to show concert_id of each follow_up in the volunteer form. in this case you should add
accepts_nested_attributes_for :follow_ups in your volunteer.rb
then:
<%= form_for #volunteer do |f| %>
<%= f.fields_for :follow_ups do |form_builder| %>
<%= label_tag "custom_label", "follow up id : #{form_builder.object.id}, concert_id : #{form_builder.object.concert_id}%>
<% end %>
<% end %>
The fields_for helper will iterate through all follow_ups , then you can get the object for each follow_up using object which allow you to deal with object directly and get your concert_id attribute from it.

Rails: Feedback by users on other users

I'm using Rails 4 and am having trouble figuring out how to set up my models so that one user can leave feedback for another user.
I have tried to follow the answer to the attached question: rails 4 users reviews for users how to do this
My models are: User, Profile, Feedback
The associations between models are:
User.rb
has_one :profile
has_many :feedbacks
accepts_nested_attributes_for :feedbacks
Profile.rb: belongs_to :user
Feedback.rb:
belongs_to :user
has_one :reviewer, :class_name => 'User', :foreign_key => 'reviewer'
The feedback table has attributes called :comment (text), :reviewer (integer) and :created_at(datetime). The reviewer needs to be the user id of the user who left the feedback.
The user model has attributes for :first_name and :last_name.
In my profile show page, I have a link to the feedback partial:
<%= render "feedbacks/feed" %>
That partial then has:
<div class="containerfluid">
<div class="row">
<div class="col-md-10 col-md-offset-1">
<% if can? :read, #feedback && #user.feedback.comment.!blank %>
<%= #profile.user.feedbacks.each do |feedback|%>
<div class="row">
<div class="col-md-10 col-md-offset-1">
<%= #feedback.comment %>
</div>
</div>
<div class="row">
<div class="col-md-3">
<div class="logobox">
<%= "#{#user.feedback.reviewer.first_name} #{#user.feedback.reviewer.last_name}" %>
</div>
</div>
<div class="col-md-3">
<div class="logobox">
<%= #feedback.try(:created_at).try(:strftime, '%d %B %Y') %>
</div>
</div>
<% end %>
<% else %>
<%= render html: "No feedback available".html_safe %>
</div>
<% end %>
</div>
</div>
</div>
What I want from the above is to display the feedback to any user who can read the feedback on the user's (receiving the feedback) profile page. If there is no feedback or the current_user cannot view the feedback, then the else statement should be applied.
I have several problems with the above:
I don't know how to traverse models to link to the feedback. The feedback partial is being displayed in the profile show page. Feedback belongs to user and profile belongs to user.
I am getting an error pointing to the first line of the above (being <% if can? :read, #feedback && user.feedback.comment.!blank? %>. The error says it is expecting 'then'. I have never used 'then' before and it doesn't work when i just type that after blank.
I have an integer attribute in my feedbacks table called :reviewer. I want to store the user id of the user who created the feedback (on the other user) in that field. I don't know how to do this. The SO post copied above suggests I put a line in my feedback model which says: has_one :reviewer, :class_name => 'User', :foreign_key => 'reviewer'. I've tried this but I can't understand what it is doing.
Can anyone help? Perhaps there is another way to approach this problem and I would appreciate help finding it. The SO post I did manage to find was voted too broad, but I can't find any other references with more specific aspects of this problem set out.
My approach would have been using polymorphic associations. Create a polymorphic model.
class Feedback < ActiveRecord::Base
belongs_to :reviewer, polymorphic: true
belongs_to :reviewable, polymorphic: true
end
After my polymorphic model is set up I create the concerns which you can include as behaviours in your user model.
module Reviewer
extend ActiveSupport::Concern
included do
has_many :given_reviews, as: :reviewer, dependent: :destroy
end
end
module Reviewable
extend ActiveSupport::Concern
included do
has_many :received_reviews, as: :reviewable, dependent: :destroy
end
end
Your user model should look something like this.
class User
include Reviewer #user is behaving as a reviewer
include Reviewable #user is behaving as a reviewable entity
end
After all this is set up you have a reusable module to start with which will work with any other model as well.
userA.given_reviews #reviews posted by the user A
userA.received_reviews #reviews other people have given about user A
The feedback class is a join from user-reviewer. It should have two foreign keys on it. For user and reviewer. Make them both belongs_to.
User.rb
has_one :profile
has_many :feedbacks
has_many :reviewers, :through => :feedbacks, :class_name => 'User'
has_many :created_feedbacks, :class_name => 'Feedback', :foreign_key => 'reviewer_id'
has_many :reviewed_users, :through => :feedbacks_left, :source => :user, :class_name => 'User'
# Not sure if you need accepts nested attributes.
Feedback.rb:
belongs_to :user
belongs_to :reviewer, :class_name => 'User' # foreign_key should be reviewer_id
Note - if you use has_one or has_many, it expects the foreign key to be on the associating class. In the way you have it written up, that would mean the feedback_id is on the user, but that doesn't make sense. Make it a belongs_to instead.
Change reviewer to reviewer_id and add it to the feedback model. Then the reviewer method generated by the database column won't clobbered by the reviewer method that comes from the belongs_to.
After that, you'll be good.

ActiveRecord query to join Posts, post authors and likes in Rails 4

So, I have read through quite a few rails active records pages, stack O questions and answers (about 12 hours of time) trying to figure out how the heck to tie all of these things together into a single query to display them on my page.
Here is my page view
Secrets with owner info
</h3>
<% #secretInfo.each do |i| %>
<p><%= i.content %> - <%= i.first_name %></p>
<p><%= i.created_at %></p>
--> "this is where I'd like to have likes for post" <--
<% end %>
and here is my controller
def show
#user = User.find(params[:id])
#secrets = Gossip.all
#mySecrets = Gossip.where(user_id: [params[:id]])
#secretInfo = Gossip.joins(:user).select("content", "first_name", "created_at")
#secretWLikesNInfo = WTF MATE?
end
Also, may help to see my models and schema so here are those
class User < ActiveRecord::Base
attr_accessor :password
has_many :gossips
has_many :likes
has_many :liked_secrets, :through => :gossips, :source => :gossip
class Like < ActiveRecord::Base
belongs_to :user
belongs_to :gossip
class Gossip < ActiveRecord::Base
belongs_to :user
has_many :likes
has_many :liking_users, :through => :likes, :source => :user
I don't know why this seems so impossible or it could be something very simple that I am just overlooking. This all was very easy in PHP/MySQL. All help is appreciated.
Additional points for coming up with a query that allows me to see all posts that I as a user has created AND liked!
Well, what you want to do is eager loading: load data associated with a record in a single roundtrip to the database. For example, i think you can load all your data like this:
#user = User.where(id: params[:id])
.joins(:liked_secrets)
.includes(:liked_secrets => :likes)
.first!
#secretInfo = #user.liked_secrets.map do |secret|
OpenStruct.new(
content: secret.content,
first_name: user.first_name,
created_at: secret.created_at,
likes: secret.likes
)
end
This works by including in the data fetched from the database in the first query all the data associated included in the include parameter. So, calling #user.liked_secrets will return the secrets but won't call the database because that information already came from the database in the first query. The same happens if you do #user.liked_secrets.first.likes because of the :linked_secrets => :likes parameter on the initial query.
I'll let a link to a good blog post about this here:
http://blog.arkency.com/2013/12/rails4-preloading/.
And, if you feel the Rails ORM (ActiveRecord) doesn't really works for your use case, you can just use sql in a string or fallback to use another Ruby ORM out there (like Sequel).

rails relationship. Rails 4

I'm new in the world of rails developers. Please, help me to understand.
I've 3 tables:
Calls, Questions, Results
Calls is:
id, name, date
Questions is:
id, question
Results is:
id, call_id, question_id, result
I've read the Rails manual, as i understand i've created 3 models.
In my model Call.rb
I've done next relationship:
has_many :results
has_many :question, through: :results
My Result.rb
belongs_to :call
belongs_to :question
My Question.rb
has_many :result
So, there are can be many records in the table "results" with one call_id, and it's can be one relation with question through results table
If if try to launch code like this:
#calls = Call.all
Than on my view:
<% #calls.each do |call| %>
<%= call.result.result %>
<% end %>
i've error that "result is undefined method". But it's must be a property.
What i do wrong?
Thanks!
According to your schema, your associations should look like this
class Call < ActiveRecord::Base
has_many :questions
has_many :results
end
class Question < ActiveRecord::Base
belongs_to :call
end
class Result < ActiveRecord::Base
belongs_to :call
end
So in the view,
<% #calls.each do |call| %>
<% call.results.each do |result| %>
<%= result.result%>
<% end %>
<% end %>
A few things.
First, you need to fix your associations so the plural and singular tenses match. has_many :result does not work as Marcelo points out.
Second, you need to ensure that your tables actually have the correct id's to make the associations work. Use the rails console to inspect Result. From your question info, it should have attributes for call_id and question_id. Once you've confirmed this, create a few objects in the console and test your associations.
#call = Call.create(name: "test", date: Time.now)
#result = Result.create(call_id: #call.id, result: "some result")
Then
#call.result # should yield the Result record you just created
Lastly, you need to rename the result attribute for Result. That's super confusing and will only cause problems.
The first thing I notice is that your call should have many questions, and many results through questions. That's because calls own questions, which in turn own results themselves.
class Call < ActiveRecord::Base
has_many :questions
has_many :results, through: :questions
end
You didn't need call_id in Result class. But, if you wish to keep it there, you dont need through: :questions in your call class (given there is a direct relation between them)
In your Question class, I assume it is a typo, but it should be plural
has_many :results
Having said that, your loop through calls will bring results (plural) and not result (singular) given that a call may have many results. Therefore:
<% #calls.each do |call| %>
<% call.results.each do |result| %>
<%= call.result %>
<% end %>
<% end %>

Find items with belongs_to associations in Rails?

I have a model called Kase each "Case" is assigned to a contact person via the following code:
class Kase < ActiveRecord::Base
validates_presence_of :jobno
has_many :notes, :order => "created_at DESC"
belongs_to :company # foreign key: company_id
belongs_to :person # foreign key in join table
belongs_to :surveyor,
:class_name => "Company",
:foreign_key => "appointedsurveyor_id"
belongs_to :surveyorperson,
:class_name => "Person",
:foreign_key => "surveyorperson_id"
I was wondering if it is possible to list on the contacts page all of the kases that that person is associated with.
I assume I need to use the find command within the Person model? Maybe something like the following?
def index
#kases = Person.Kase.find(:person_id)
or am I completely misunderstanding everything again?
Thanks,
Danny
EDIT:
If I use:
#kases= #person.kases
I can successfully do the following:
<% if #person.kases.empty? %>
No Cases Found
<% end %>
<% if #person.kases %>
This person has a case assigned to them
<% end %>
but how do I output the "jobref" field from the kase table for each record found?
Maybe following will work:
#person.kase.conditions({:person_id => some_id})
where some_id is an integer value.
Edit
you have association so you can use directly as follows:
#kases= #person.kases
in your show.rhtml you can use instance variable directly in your .rhtml
also #kases is an array so you have to iterate over it.
<% if #kases.blank? %>
No Kase found.
<% else %>
<% for kase in #kases %>
<%=h kase.jobref %>
<% end %>
<% end %>
If your person model has the association has_many :kases then, you can get all the cases that belongs to a person using this
#kases = Person.find(person_id).kases
Assuming person_id has the id of the person that you want to see the cases for.
You would probably want something like
has_many :kases
in your Person model, which lets you do #kases = Person.find(person_id).kases
as well as everything else that has_many enables.
An alternate method would be to go through Kase directly:
#kases = Kase.find_all_by_person_id(person_id)

Resources