请参阅问题底部的更新...
作为参考,这个问题是根据我在此处遇到的上一个问题做出的一些修正而演变而来的: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
当我点击“提交”创建新用户时,它现在会将我发送到以下网址,这似乎是正确的:localhost:3000 / users / 6 / profile / new。但它引发了以下异常:
NoMethodError in ProfilesController #new你有一个 当你没想到它时,没有对象! 评估时发生错误 nil.build
跟踪表明问题出在Profile方法的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中的ActiveRecord :: RecordNotFound #index
找不到没有ID的用户
这是日志告诉我的:
处理ProfilesController #index [GET]参数: { “动作”=> “中的索引”, “controller”=>“个人资料”}
ActiveRecord :: RecordNotFound(不能 找不到身份证的用户):
应用程序/控制器/ profiles_controller.rb:5:在 `索引'
为了向您提供有关应用程序的其他详细信息,模型具有以下关联:
个人资料属于_to:用户
用户有_one:个人资料
我在routes.rb文件中有这个:map.resources:users,:has_one => :个人资料
在新的个人资料页面的视图中抛出了上面列出的第一个例外,我有这个:
<% 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,根据您的要求:
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 =&gt; 'users',:action =&gt; “新”
map.logout'logout',: controller =&gt; 'sessions',:action =&gt; “摧毁”
map.login'login',: controller =&gt; 'sessions',:action =&gt; “新”
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(截至2009年8月20日,美国东部时间晚上8点)
class ProfilesController&lt; 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,下面的索引页面抛出以下异常:
配置文件中的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>
答案 0 :(得分:5)
错误告诉你这一行:
@profile = @user.profile.build
@ user.profile是nil。
由于您尚未创建配置文件,因此这是有道理的。相反,去做这样的事情。
@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 -%>
答案 1 :(得分:1)
@user.profiles.build
@user.profiles.create
当用户有一个个人资料时,您必须使用:
@user.build_profile
@user.create_profile
无法确定,请检查API
答案 2 :(得分:0)
@jdl是正确的,因为在ProfilesController中没有@ user.profile对象#new,所以调用@ user.profile.build会抛出一个零异常。
您可以通过
实例化新的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插件,但要点是您要加载对象的LIST,而不仅仅是一个。在您看来,如果您正在迭代@users并在该列表的元素上调用.profile,那么ActiveRecord将动态加载相关的配置文件。
#show非常相同 - 无需显式加载配置文件。
def show
@user = User.find(params[:user_id])
end
答案 3 :(得分:0)
做一件事
在ProfileController
提供session[:user_id]
而不是params[:user_id]
def show <br>
@user = User.find(session[:user_id])<br>
@profile = @user.profile <br>
end <br>
我希望它有效!