【问题标题】:ActiveRecord::RecordNotFound -- Couldn't find User without an IDActiveRecord::RecordNotFound -- 找不到没有 ID 的用户
【发布时间】:2010-11-21 04:45:06
【问题描述】:

请参阅问题底部的更新...

作为参考,这个问题是由我根据之前在这里遇到的问题所做的一些修复演变而来的:Associating Two Models in Rails (user and profile)

我正在构建一个具有用户模型和配置文件模型的应用程序。

我想这样关联这些模型:
- 用户创建帐户后,会自动转到“创建个人资料”页面,并且他创建的个人资料仅与该特定用户关联。
- 只有拥有个人资料的用户才能对其进行编辑。

我使用 nifty_generators 生成了用户模型。当用户点击提交以创建帐户时,我将他重定向到“新配置文件”视图以创建配置文件。我通过在用户控制器中编辑重定向路径来做到这一点。用户控制器如下所示:

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

当我单击“提交”以创建新用户时,它现在将我发送到以下 url,这似乎是正确的:localhost:3000/users/6/profile/new。但它会引发以下异常:

NoMethodError 在 ProfilesController#new 你有一个 nil 对象,当你没想到它! 评估时发生错误 nil.build

跟踪表明问题出在配置文件控制器中,在 New 方法中。配置文件控制器如下所示:

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

此外,当我尝试查看配置文件的索引页面时,该应用程序也会引发异常(当前没有配置文件,因为我无法通过用户创建步骤来创建一个)。这是一个例外:
ProfilesController#index
中的 ActiveRecord::RecordNotFound 找不到没有 ID 的用户

这是日志告诉我的:

Processing ProfilesController#index [获取] 参数: {"动作"=>"索引", “控制器”=>“配置文件”}

ActiveRecord::RecordNotFound(不能 查找没有 ID 的用户):
应用程序/控制器/profiles_controller.rb:5:in `索引'

为了向您提供应用程序的其余详细信息,模型具有以下关联:
个人资料属于 _to :user
用户有 _one :profile

我在 routes.rb 文件中有这个:map.resources :users, :has_one => :profile

在抛出上面列出的第一个异常的新个人资料页面的视图中,我有这个:

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

在引发上述第二个异常的配置文件索引视图中,我有这个:

<% @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>

我已经花了几个小时试图自己找出问题作为学习练习,但目前我不知道如何解决这个问题。感谢您的帮助!

更新:

jdl,根据您的要求:
配置文件/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 做 |map|
map.signup 'signup', :controller => 'users', :action => 'new'
map.logout '注销', :controller => 'sessions', :action => 'destroy'
map.login 'login', :controller => 'sessions', :action => 'new'
map.resources:会话

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

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

配置文件控制器(截至 2009 年 8 月 20 日,美国东部标准时间晚上 8 点)
类 ProfilesController

  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,下面的索引页面抛出以下异常:
配置文件中的 NoMethodError#index
显示第 14 行出现的 app/views/profiles/index.html.erb:
# 的未定义方法“名称”

    <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> 

【问题讨论】:

  • 你的路线是什么样的?专门针对用户和个人资料
  • 也许:redirect_to new_user_profile_path(:user_id => @user.id)
  • 好问题,顺便说一句。用适量的示例代码和对问题的清晰解释写得很好。新用户注意了。
  • Cody,我在 routes.rb 文件中有这个:map.resources :users, :has_one => :profile

标签: ruby-on-rails controller


【解决方案1】:

做一件事

ProfileController 中提供session[:user_id] 而不是params[:user_id]

def show <br>

@user = User.find(session[:user_id])<br>
@profile = @user.profile <br>
end <br>

我希望它有效!

【讨论】:

    【解决方案2】:

    错误告诉你这一行:

    @profile = @user.profile.build
    

    @user.profile 为零。

    由于您尚未创建个人资料,因此这是有道理的。相反,请选择这样的东西。

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

    Re:索引页面抛出异常。

    您在控制器中定义@users,然后在路径助手中引用@user。此外,迭代 @users 应该给你 User 对象,而不是 Profile 对象。

    您似乎正在尝试使用 Profile#index 操作来显示用户列表。没关系,以一种不太纯的 REST 方式。不过,我希望看到更多这样的东西。

    <% @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 -%>
    

    【讨论】:

    • 好吧,无论谁投反对票,都应该解释为什么......我认为这是一个合理的猜测
    • jdl,当我进行更改时,出现以下异常:ActionView::TemplateError (undefined method `user_profiles_path' for #<:base:0x2651eac>) on line #3 of app/ views/profiles/new.html.erb:
      这是导致问题的原因,但我不知道如何解决它:
      我尝试删除@user,但没有奏效。有什么建议吗?
    • 你能发布 new.html.erb 和你的 routes.rb 文件吗?这应该很容易追踪,但除非我们能看到有问题的代码,否则这只是猜测。
    【解决方案3】:

    hmm.. 我想当用户有很多个人资料时,你可以使用:

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

    当用户有一个个人资料时,您必须使用:

    @user.build_profile
    @user.create_profile
    

    不能确定,检查 API

    【讨论】:

      【解决方案4】:

      @jdl 是正确的,因为 ProfilesController#new 中没有 @user.profile 对象,因此调用 @user.profile.build 会抛出 nil 异常。

      您可以通过实例化一个新的 Profile 对象来解决此问题

      @user.profile = Profile.new
      

      稍后如果@user 被保存,那么它将触发@user.profile 并且外键将被适当地设置。

      同样在您的 ProfilesController 中,#index 和 #show 操作有点奇怪。按照惯例,#index 操作返回一个“对象列表”,而#show 只显示一个对象,一个给定 ID 的特定对象。但是,您的 #index 返回一个非常具体的对象,因为它执行具有特定 ID 的 User.find。此外,由于用户只有一个 Profile 对象,因此也使用 ORDER BY 加载其 Profile 对象是没有意义的。应该只有一个,所以不需要订单。最重要的是,是否需要显式加载配置文件对象存在争议,因为您可以通过 @user.profile 访问它,而 ActiveRecord 将按需加载它。所以你的新 #index 看起来像

      def index
        @users = User.paginate(:all, :order = "created_at desc", :page => 1, :per_page => 10)
      end
      

      这假设您有 WillPaginate 插件,但要点是您正在加载一个对象列表,而不仅仅是一个。在您看来,如果您正在迭代 @users 并在该列表的一个元素上调用 .profile ,那么 ActiveRecord 将动态加载关联的配置文件。

      #show 完全一样 - 无需显式加载配置文件。

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

      【讨论】:

      • 科迪,在你的解释中,这条线到底在哪里? @user.profile = Profile.new
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-08-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多