code_crusher code_crusher - 1 month ago 13
Ruby Question

Error getting user's name and email from the associated model in Ruby on Rails

I am creating a webiste where people can debate with each other. It has 4 main models - post, for_the_motion, against_the_motion, and user( added in the respective order). I ran a migration and made a association between for model and against model.

For each view in "for" model I want to show which user added that particular motion. But I am getting an error


undefined method `image_url' for nil:NilClass


Stuck from long time on this. This is how the models look

user.rb

class User < ApplicationRecord
has_many :posts
has_many :fors
has_many :againsts
class << self
def from_omniauth(auth_hash)
user = find_or_create_by(uid: auth_hash['uid'], provider: auth_hash['provider'])
user.name = auth_hash['info']['name']
user.image_url = auth_hash['info']['image']
user.url = auth_hash['info']['urls'][user.provider.capitalize]
user.save!
user
end
end


end

for.rb

class For < ApplicationRecord
belongs_to :post, optional: true
belongs_to :user,optional: true
end


post.rb

class Post < ApplicationRecord
has_many :fors, dependent: :destroy
has_many :againsts, dependent: :destroy
belongs_to :user, optional: true
end


against.rb

class Against < ApplicationRecord
belongs_to :post, optional: true
belongs_to :user, optional:true
end


CONTROLLERS

posts_controller.rb

class PostsController < ApplicationController
def index
@posts = Post.all
end

def land
end

def show
@post = Post.find(params[:id])
end

def new
@post = Post.new
end

def create
@post = Post.new(post_params)
@post.user = current_user

if @post.save
redirect_to @post
else
render 'new'
end
end
private
def post_params
params.require(:post).permit(:title)
end

end


fors_controller.rb

class ForsController < ApplicationController

def create
@post = Post.find(params[:post_id])
@for = @post.fors.create(fors_params)
@for.user = current_user
redirect_to post_path(@post)
end



private
def fors_params
params.require(:for).permit(:content)
end

end


sessions_controller.rb

class SessionsController < ApplicationController
def create
begin
@user = User.from_omniauth(request.env['omniauth.auth'])
session[:user_id] = @user.id
# flash[:success] = "Welcome, #{@user.name}!"
rescue
# flash[:warning] = "There was an error while trying to authenticate you..."
end
redirect_to root_path

def destroy
if current_user
session.delete(:user_id)
# flash[:success] = 'See you!'
end
redirect_to root_path
end
end
end


This is where I am getting the error

<h1><%=@post.title%></h1>
<div class="fort">
<h3>For the motion</h3>
<%@post.fors.each do |f|%>
<p><%=f.content%></p>
<p><%=f.user.image_url%></p>/*This is where errors arise*/
<%end%>
<%= render "fors/form"%>
</div>
<div class="against">
<h3>Against the motion</h3>
<%@post.againsts.each do |f|%>
<p><%=f.content%></p>
<p><%= @post.user.name%></p>
<%end%>

<%= render "againsts/form"%>
</div>


Here is the github link for any other required information
https://github.com/sarfrazbaig/DebatingSociety2

Answer Source

Seems like you missed saving the .user on fors_controller.rb:

class ForsController < ApplicationController

  def create
    @post = Post.find(params[:post_id])
    @for = @post.fors.create(fors_params)
    # .create above already will save a new For record in DB
    # therefore your @for.user assignation will be only assigned in memory, but not yet in DB
    @for.user = current_user
    # you'll need to save it again afterwards:
    @for.save
    redirect_to post_path(@post)
  end

  # ...
end

Suggestion:

  • use .new instead of .create to not-yet-save into the DB, and only call save when everything that you need to assign is already assigned.

    class ForsController < ApplicationController
    
      def create
        @post = Post.find(params[:post_id])
        @for = @post.fors.new(fors_params)
        @for.user = current_user
        @for.save
        redirect_to post_path(@post)
      end
    
      # ...
    end
    
  • Take note that you would still encounter that error even if you already updated your code with the above; this is because currently your For records in the DB all are missing the .user value. You'll have to manually assign and save the .user accordingly for each For record, and probably best that you'd write a...

    class For < ApplicationRecord
      validates :user, presence: true
    end
    

... validation so that this error will be prevented in the future.