Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ActiveRecord::RecordNotFound -- Couldn't find User without an ID

Please see UPDATES at the bottom of the question...

For reference, this problem evolved out of some fixes I made based on a previous problem I was having here: Associating Two Models in Rails (user and profile)

I'm building an app that has a user model and a profile model.

I want to associate these models such that:
- After the user creates an account, he is automatically sent to the "create profile" page, and the profile he creates is connected to only that particular user.
- Only the user who owns the profile can edit it.

I generated the user model using nifty_generators. When the user hits submit for the account creation, I redirect him to the "new profile" view to create a profile. I did this by editing the redirect path in the user controller. The user controller looks like this:

def new
  @user = User.new
end

def create
  @user = User.new(params[:user])
  if @user.save
    session[:user_id] = @user.id
    flash[:notice] = "Thank you for signing up! You are now logged in."
    redirect_to new_user_profile_path(:user_id => @user)
  else
    render :action => 'new'
  end
end

When I click 'submit' to create a new user, it now sends me to the following url, which seems right: localhost:3000/users/6/profile/new. But it throws the following exception:

NoMethodError in ProfilesController#new You have a nil object when you didn't expect it! The error occurred while evaluating nil.build

The trace indicates that the problem is in the profiles controller, in the New method. The profiles controller looks like this:

def index
  @user = User.find(params[:user_id])
  @profile = @user.profile(:order => "created_at DESC")
end

def show
  @user = User.find(params[:user_id])
  @profile = @user.profile.find(params[:id])
end

def new
  @user = User.find(params[:user_id])
  @profile = @user.profile.build
end

def edit
  @user = User.find(params[:user_id])
  @profile = @user.profile.find(params[:id])
end

def create
  @user = User.find(params[:user_id])
  @profile = @user.profile.build(params[:profile])
    if @profile.save
      flash[:notice] = 'Profile was successfully created.'
      redirect_to(@profile)
    else
      flash[:notice] = 'Error.  Something went wrong.'
      render :action => "new"
    end
end

Additionally, the app also throws an exception when I try to view the index page of the profiles (there are currently no profiles because I can't get past the user creation step to create one). This is the exception:
ActiveRecord::RecordNotFound in ProfilesController#index
Couldn't find User without an ID

This is what the log is telling me:

Processing ProfilesController#index [GET] Parameters: {"action"=>"index", "controller"=>"profiles"}

ActiveRecord::RecordNotFound (Couldn't find User without an ID):
app/controllers/profiles_controller.rb:5:in `index'

To give you the rest of the details on the app, the models have the following associations:
Profile belongs _to :user
User has _one :profile

I have this in the routes.rb file: map.resources :users, :has_one => :profile

In the view for the new profile page that's throwing the first exception listed above, I have this:

<% form_for([@user, @profile]) do |f| %>
  <%= f.error_messages %>
....
<% end %>  

In the view for the profile index that's throwing the second exception explained above, I have this:

<% @profiles.each do |profile| %>
<div class="post">
    <div class="left">
        <p>Store: </p>
        <p>Category: </p>
    </div>
    <div class="right">
        <p><%=h profile.name %></p>
        <p><%=h profile.category %></p>
    </div>
    <div class="bottom">
        <p><%= link_to 'Go to profile', user_profile_path(@user, profile) %></p>
        <p><%= link_to 'Edit', edit_user_profile_path(@user, profile) %></p>
        <p><%= link_to 'Destroy', user_profile_path(@user, profile), :confirm => 'Are you sure?', :method => :delete %></p>
    </div>

I've spent hours trying to track down the problem myself as a learning exercise, but at this point I have no idea how to fix this. Appreciate the help!

UPDATE:

jdl, per your request:
profiles/new.html.erb:

<% form_for([@user, @profile]) do |f| %>
  <%= f.error_messages %>



<div class="left">
  <p>
    <%= f.label :name %><br />
    <%= f.text_field :name %>required
  </p>
    <p>
    <%= f.label :category %><br />
    <%= f.text_field :category %>required
  </p>
  <p>
    <%= f.label :address1 %><br />
    <%= f.text_field :address1 %>
  </p>
  <p>
    <%= f.label :address2 %><br />
    <%= f.text_field :address2 %>
  </p>
  <p>
    <%= f.label :city %><br />
    <%= f.text_field :city %>
  </p>
  <p>
    <%= f.label :state %><br />
    <%= f.text_field :state %>
  </p>
  <p>
    <%= f.label :zip %><br />
    <%= f.text_field :zip %>required
  </p>
  <p>
    <%= f.label :phone %><br />
    <%= f.text_field :phone %>
  </p>
  <p>
    <%= f.label :email %><br />
    <%= f.text_field :email %>
  </p>

  </div>

  <div class="right">
  <p>
    <%= f.label :website %><br />
    <%= f.text_field :website %>
  </p>
  <p>
    <%= f.label :description %><br />
    <%= f.text_area :description %>
  </p>
  </div>

  <p>
    <%= f.submit 'Create' %>
  </p>
    <% end %> 

routes.rb:
ActionController::Routing::Routes.draw do |map|
map.signup 'signup', :controller => 'users', :action => 'new'
map.logout 'logout', :controller => 'sessions', :action => 'destroy'
map.login 'login', :controller => 'sessions', :action => 'new'
map.resources :sessions

  map.resources :users, :has_one => :profile  

  map.root :controller => "home"   
  map.connect ':controller/:action/:id'  
  map.connect ':controller/:action/:id.:format'  
end  

PROFILES CONTROLLER (as of 8/20/09, 8pm EST)
class ProfilesController < ApplicationController

  def index
    @users = User.all(:order => "created_at DESC")
  end

  def show
    @user = User.find(params[:user_id])
  end

  def new
    @user.profile = Profile.new
  end

  def edit
    @user = User.find(params[:user_id])
    @profile = @user.profile.find(params[:id])
  end

  def create
    @user = User.find(params[:user_id])
    @profile = @user.profile.build(params[:profile])
      if @profile.save
        flash[:notice] = 'Profile was successfully created.'
        redirect_to(@profile)
      else
        flash[:notice] = 'Error.  Something went wrong.'
        render :action => "new"
      end
  end

  def update
    @profile = Profile.find(params[:id])
      if @profile.update_attributes(params[:profile])
        flash[:notice] = 'Profile was successfully updated.'
        redirect_to(@profile)
      else
        render :action => "edit"
      end
  end

  def destroy
    @profile = Profile.find(params[:id])
    @profile.destroy
      redirect_to(profiles_url)
  end
end

Cody, the index page below is throwing the following exception:
NoMethodError in Profiles#index
Showing app/views/profiles/index.html.erb where line #14 raised:
undefined method `name' for #

    <div id="posts">
<% @users.each do |profile| %>
    <div class="post">
        <div class="left">
            <p>Store: </p>
            <p>Category: </p>
        </div>
        <div class="right">
            <p><%=h profile.name %></p>
            <p><%=h profile.category %></p>
        </div>
        <div class="bottom">
            <p><%= link_to 'Go to profile', user_profile_path(@user, profile) %></p>
            <p><%= link_to 'Edit', edit_user_profile_path(@user, profile) %></p>
            <p><%= link_to 'Destroy', user_profile_path(@user, profile), :confirm => 'Are you sure?', :method => :delete %></p>
        </div>
  </div> 
like image 388
MikeH Avatar asked Aug 20 '09 04:08

MikeH


2 Answers

The error is telling you that in this line:

@profile = @user.profile.build

@user.profile is nil.

Since you haven't created the profile yet, this makes sense. Instead, go for something like this.

@profile = Profile.new(:user_id => @user.id)

Re: The index page throwing an exception.

You are defining @users in your controller, and then referencing @user in your path helpers. Also, iterating over @users should give you User objects, not Profile objects.

It looks like you're trying to use the Profile#index action to show a list of users. That's kind-of OK, in a not-quite-pure-REST way. However, I would expect to see something more like this.

<% @users.each do |user| -%>
  <% unless user.profile.blank? -%>
    <%= h user.profile.name %>
    <%= h user.profile.category %>
    <%= link_to 'Go to profile', user_profile_path(user, user.profile) %>
  <% end -%>
<% end -%>
like image 88
jdl Avatar answered Oct 17 '22 20:10

jdl


hmm.. I thought when User has many profiles, you can then use:

@user.profiles.build
@user.profiles.create

when User has one profile, you have to use:

@user.build_profile
@user.create_profile

Can't be sure, check API

like image 21
penger Avatar answered Oct 17 '22 21:10

penger