Rails Undefined method for ni:NilClass Error? - ruby-on-rails

I have a rails app where I have has_many and belongs_to association. But I am having problems while trying to retrieve the elements. My Models are Events, Comments and Votes.
Events has multiple Comments and Votes. Of Course Comments and Voted belong_to one event.
My schema is
create_table "events",
t.string "etime"
t.integer "eid"
end
create_table "votes",
t.integer "eventid"
t.string "userid"
t.integer "event_id"
end
Associations:
class Event < ActiveRecord::Base
has_many :comments
has_many :votes
end
class Votes < ActiveRecord::Base
belongs_to :event
end
I am trying to view all the votes for the events. The controller action is:
#events = Event.all
#events.each do |event|
event.votes.each do |vote|
respond_to do |format|
format.html
end
end
end
View and error line:
<%= #vote.userid %>
I get an error "Undefined Method "userid" for vote". In my controller, I render my eventid and it worked. So it seems to be a problem when I do it in the view..
Any idea What I could be doing wrong? I am completely lost on this one

Might be as simple as using event_id instead of eventid.
It's always worth it to run rails console and play around in there. You can usually autocomplete methods.
v = Vote.new
v.event<tab to get options>

Somewhere you have got it all wrong, your table migrations, as in your foreign_key names are wrong, so are your primary keys.
In standard cases, all models have primary keys as id, just id.
For associating with other model, for example, a belongs_to :event association your vote model needs a , event_id column. These are rails standards
Lets assume, your associations /columns are correct, The error that you get is because your #vote object is nil , in your controller you need to assign/initialize the #vote variable to use it the view which you don't seem to be doing.

Related

Rails: ActiveRecord Association returns a nil relationship

I'm trying to print all the comments and their associated users for a post on a blog. The comments are passed into the view, and their users_id and posts_id are accessible, but I cannot access their users; comment.user returns nil.
Model:
class Comment < ActiveRecord::Base
belongs_to :user
belongs_to :post
end
Migration:
class CreateComments < ActiveRecord::Migration
def change
create_table :comments do |t|
t.string :text
t.references :users
t.references :posts
t.timestamps
end
end
end
In the controller:
#post = Post.find(params[:id])
#comments = Comment.where(posts_id: params[:id]).order(:created_at)
In the view:
<% #comments.each do |c| %>
<% c.user.nil? %>
<p><%=c.text%></p>
<p><%=c.users_id%></p>
<p><%=c.posts_id%></p>
<% else %>
<p><%=c.text%></p>
<p><%=c.user.name%></p> #Here is where things would break if I didn't check "c.user.nil?"
<% end %>
<% end %>
The text and ids for the comments print as expected, but the user is still considered nil. Does anyone know how to access user from within the comment model in the view?
I believe the issue are the names of the foreign key columns in your database. When declaring a post association while explicitly stating the foreign key (you could do this), it's going to look for post_id on the model. So, you should either change your column in the DB to post_id, or change the association method call to this:
belongs_to :post, foreign_key: :posts_id
In your controller:
Change:
#comments = Comment.where(posts_id: params[:id]).order(:created_at)
To:
#comments = Comment.where(post_id: params[:id]).order(:created_at)
You should have a post_id column in your comments table in the database. Then, you can have access to the user for a given comment like this:
comment.user
and eventually:
comment.user.name
will work too!
Your migrations does not seems right to complete what #Jake Shorty said, I would say When doing :
t.references :users
Rails will actually transform it such as :
t.integer 'users_id'
which does not match with the default Rails conventions, so it cannot retrieve the associated user using user_id foreign key.
To fix it, you need to either change the migration, or change your model :
class Comment < ActiveRecord::Base
belongs_to :user, foreign_key: :users_id
end

activerecord attribute based on a join?

I'm building a sort of reddit clone to pick up rails again. I have a table posts and a table votes.
Posts:
create_table :posts do |t|
t.belongs_to :user
t.string :title
end
Votes:
create_table :votes do |t|
t.belongs_to :post
t.belongs_to :user
t.string :sort_of_vote
end
I want to retrieve a list of posts with a boolean attribute per post if it is liked by a user or not.
So I would like to something like:
Post.all.first.liked?
I'm thinking of a good way to do this. What I don't want: a query per liked? method call. What would be a good way to achieve this?
Read about Eager Loading Associations, specifically the section on Solution to N + 1 queries problem which uses includes
#posts = Post.includes(:votes)
Now you can create a method on Post like
def liked?
!votes.empty?
end
and call it without forcing a query for each item in the collection.
Is this what you want?
class User
def liked_posts
#liked_posts ||= self.votes.posts
end
def likes?(post)
#liked_posts.include?(post)
end
end
class Post
def liked?(user)
user.liked_posts.include?(self)
end
end
This should retrieve the list of liked posts once and cache it. Then when you call post.liked?(user) or user.likes?(post) it will be able to use the cached data to determine if the user likes that post.

Rails Theory - No Database Dependencies?

I was under the impression that with Rails you're not supposed to define any dependencies in the database, but rather just use your has_many and belongs_to stuff to define relationships. However, I'm going through the rails guide, and it has the following.
class CreateComments < ActiveRecord::Migration
def change
create_table :comments do |t|
t.string :commenter
t.text :body
t.references :post
t.timestamps
end
add_index :comments, :post_id
end
end
I thought this wasn't okay...? I'm trying to do something like a comment field that creates a new instance each time you call the show method, but I think without these "references" and "add_index," it's not storing the post_id in the comment row.
All this migration does is create post_id and tells the database that it should index this column (improves performance)
t.references :post is basically the same as t.integer :post_id so, yes, it is storing the post_id in the comment. You'll still need to define your relationships in your models.
You are actually wrong on the philosophy.
Rails magic is good, only when backed at the DB level by actual foreign keys.
The docs clearly state this
Rails magic comes in, when you have correctly named your foreign keys, so that it can use the convention to figure out the associations.
What's wrong with expressing relationships within the ORM, that's where it's supposed to be done. I believe you are getting mixed up between db vendor specifics such as foreign key constraints and relationships.
class Comment < ActiveRecord::Base
attr_accessible :post, :post_id
belongs_to :post
end
class Post < ActiveRecord::Base
has_many :comments
end
class CommentsController < ApplicationController
def create
#comment = Comment.create(params[:comment]) # where params[:comment] = {post_id: 1, message: ''}
#post = comment.post
respond_with(#comment)
end
end

Rails many to many update and create

I'm interested in creating and updating a row in table without a primary key. My table has 3 columns - person_id, year and salary. I understand that I should use has_and_belongs_to but I'm having problems understanding how to implement my create and update methods and my form.html file. Can anyone help explain this to me, perhaps with a simple example of how to do it?
has_and_belongs_to_many example
# category model
class Category < ActiveRecord::Base
has_and_belongs_to_many :users
end
# user model
class User < ACtiveRecord::Base
has_and_belongs_to_many :categories
end
join table look like
class CreateCategoriesUsersJoinTable < ACtiveRecord::Migration
def change
create_table :categories_users, :id => false do |t|
t.integer :category_id
t.integer :user_id
end
end
end
now you can accessing your information
$ User.categories
$ Category.users
$ user = User.first
$ user.categories
$ category = Category.first
$ category.users
Add a primary key, and ignore it. You can add a unique index on (person_id, year) to simulate a PK constraint, but ActiveRecord heavily relies on having ids for its instances.

how to use array variables inside action in controller

I want to fetch all posts posted by those users who have gone to same college as the current users...So inside my welcome controller i have written following code..
class WelcomesController < ApplicationController
def index
#col = Education.select(:college_id).where(:user_id => #current_user)
#user = Education.select(:user_id).where(:college_id => #col)
#welcome = Welcome.where(:user_id => #user)
end
end
Following is my shema code for welcome and education model:
create_table "welcomes", :force => true do |t|
t.text "message"
t.integer "user_id"
end
create_table "educations", :force => true do |t|
t.integer "college_id"
t.integer "user_id"
end
#col = Education.select(:college_id).where(:user_id => #current_user)....this line returns college ids associated with current logged in user.This is working perfectly on my console which is returning following output..
[#<Education college_id: 1>, #<Education college_id: 2>]
but i dont know how to use this output in my next line,so i have written this statement which should return all the users whose college id is the output of prevous statement
#user = Education.select(:user_id).where(:college_id => #col)
and my last line should return all the posts posted by those users whose ids are inside the #user array:
#welcome = Welcome.where(:user_id => #user)
but this is not working.When i run my project i cant see any output on my page and on console i am getting following output :
SELECT welcomes.* FROM welcomes WHERE (welcomes.user_id IN (NULL))
which means its not getting any user ids..
How can i solve this ...
You can try this:
#col = Education.select(:college_id).where(:user_id => #current_user.id).all
#users = Education.select(:user_id).where(:college_id => #col.collect(&:college_id)).all
#welcome = Welcome.where(:user_id => #users.collect(&:user_id)).all
The best way I see to accomplish this is to set up a has_many_and_belongs_to_many relationship between your User and Education models. (Each Education will have many Users and each User may have multiple Eductions.) You will need to create a joining table in your database to support this type of relationship - see the Rails Guide for more information on this.
I would set up your models in this manner:
class User < ActiveRecord::Base
has_one :welcome
has_and_belongs_to_many :educations
end
class Education < ActiveRecord::Base
has_and_belongs_to_many :users
end
class Welcome < ActiveRecord::Base
belongs_to :user
end
The join table for the has_many_and_belongs_to_many join table migration (be sure to double check this code, not sure I got this exactly right):
def self.up
create_table 'education_user', :id => false do |t|
t.column :education_id, :integer
t.column :user_id, :integer
end
end
Your controller code is now much simpler and looks like this:
#welcomes = #current_user.eductions.users.welcome.all
In your view:
<% #welcomes.each do |welcome| %>
<p><%= welcome.message %></p>
<% end %>
One of the more powerful features of Ruby on Rails is the model relationships. They are a little more work up front, but if you take the time to set them up correctly they can make your life much easier, as is evidenced by the simplified #welcomes query above.
I'd recommend you to make relation between User and Collage
class User < ActiveRecord::Base
has_many :educations
has_many :colleges, :through => :educations
has_many :posts
scope :by_college_id, lambda {|cid| where("exists (select educations.id from educations where educations.user_id = users.id AND educations.college_id in (?) limit 1)", Array.wrap(cid)) }
def college_mates
self.class.by_college_id(self.college_ids).where("users.id != ?", id)
end :through => :educations
end
class Education < ActiveRecord::Base
belongs_to :user
belongs_to :college
end
So now in your controller you can write
class WelcomesController < ApplicationController
def index
#posts = #current_user.college_mates.includes(:posts).map(&:posts).flatten
# or
#posts = Post.where(user_id: #current_user.college_mates.map(&:id))
end
end
Second variant generates 3 sql-requests, first variant - only two. But this is same work with data, I think time will be also same. Usually controllers contain only few lines of code, all logic written in models. Ideally controller should contain only Post.by_college_mates_for(#curren_user.id)

Resources