源码地址https://github.com/runup/blog 环境是 ruby1.9.3 rails 3.2.13 数据库 sqlite3
要实现一个文章评论的功能http://localhost:3000/twos/23 但是不能实现,评论在提交的时候,two_id 一直显示为空,就是说这个字段没有提交成功 其中 comment(评论),two(文章),user(用户),他们的关系为 two belongs to user comment belongs to user,comment belongs to two ,如图所示。
class CommentsController < ApplicationController
def create
@user = User.find(current_user)
@two = @user.twos.find(params[:id])
@comment = @user.comments.create(params[:comment])
redirect_to root_path
end
end
http://localhost:3000/twos/23 提交的话 报错,Couldn't find Two without an ID 如果去掉@two = @user.twos.find(params[:id]) 数据保存成功,但是数据库中是没有 two_id 这个字段的值为空,说明没有传递成功。
同学 下个断点看看@two
找到了么~ 不出意外你的这个功能是评论别人的文章吧 别人的文章用自己的current_user
怎么会找到@two
呢...... 应该是直接Two.find(prams[:id])
吧
@zj0713001
def create
@user = User.find(current_user)
@two = @user.twos.find(params[:id])
@comment = @user.comments.build(:user_id=>@user.id,:two_id=>@two.id)
redirect_to root_path
end
使用您说的方法 Two.find(prams[:id]),还是报 Couldn't find Two without an ID 这个错误。
非常感谢。
跟你之前那个帖是同样地问题啊。 首先你这里的
@user = User.find(current_user)
取到的是当前用户,但是你之后的
@two = @user.twos.find(params[:id])
这行里,params[:id] 取到的 id 是当前页面的 two 的 id。问题在于,你取到的当前用户是要添加 comment 的用户,而不一定是拥有这个 two 的用户,所以出现 Couldn't find Two without an ID。 而要取得当前页面的 two,可以直接
@two = Two.find(params[:id])
然后
@comment = @two.comments.new(user_id: current_user, ...)
你非要反过来的话就得这样
@user = User.find(current_user)
@two = Two.find(params[:id])
@comment = @user.new(two_id: @two.id, ...)
不过要记得,必须要手动指定 user_id,或者 two_id,因为这两个属性不会存在 params[:comment] 里面,另外,Rails 3.2.8 之后 4.0.0 之前,要用 params[:comment] 要记得加上
attr_accessible: :aaa, :bbb, :ccc
差不多就这样,这些都很基础的,LZ 你先把官方的 Guides 过一遍,基本就懂了。
@messiahxu @zj0713001 非常感谢你们。我是个新手,官方文档在看。 class CommentsController < ApplicationController def create @user = User.find(current_user) @two = Two.find(params[:id]) @comment = @two.comments.build(:user_id=>@user.id,:two_id=>@two.id) redirect_to root_path end end 可是还是报错 Couldn't find Two without an ID 能不能帮我看下哪里的问题?
#11 楼 @runup 这个方法能过... 不过我不推荐,我觉得应该用 nested resource 不过要先去吃饭 ...
comments_controller.rb:
class CommentsController < ApplicationController
def create
@user = User.find(current_user)
@two = @user.twos.find(params[:two_id]) # params 改成这个
@comment = @user.comments.create(params[:comment])
redirect_to root_path
end
end
app/views/twos/show.html.erb
文章标题:<%[email protected]%>
<br>
文章内容</br>
<%[email protected]%>
</br>
<%= link_to '返回文章列表',twos_path%>
<h2>文章评论评论</h2>
<%= form_for(@comment) do |f| %>
<div class="field">
<%= f.label :评论 %><br />
<%= f.text_field :comment %>
</div>
<%= hidden_field_tag(:two_id, @two.id) %> # 加这行
<div class="actions">
<%= f.submit :提交%>
</div>
<% end %>
twos_controller.rb
def show
@user = User.find(current_user)
@two = @user.twos.find(params[:id])
# @comment = @user.comments.build(:user_id=>@user.id,:two_id=>@two.id)
@comment = @two.comments.build(:user_id=>@user.id)
end
改这样试试行不行
resources :users resources :twos resources :comments routes 我是这么写的,但是我之前测试过没有什么问题。
#21 楼 @runup 那是因为你的 Comment#create
里面没有把 two
的 id 传给要创建的 comment 啊...
comments_controller.rb
class CommentsController < ApplicationController
def create
@user = User.find(current_user)
@two = @user.twos.find(params[:two_id])
@comment = @user.comments.build(params[:comment])
@comment.two = @two
@comment.save
redirect_to root_path
end
end
或者
class CommentsController < ApplicationController
def create
@user = User.find(current_user)
@two = @user.twos.find(params[:two_id])
@comment = @two.comments.build(params[:comment])
@comment.user = @user
@comment.save
redirect_to root_path
end
end
@messiahxu 您好。 非常感激您的建议,我看了这篇官方文档http://guides.rubyonrails.org/association_basics.html ,并没有出现您说的这个知识点“必须要手动指定 user_id,或者 two_id,因为这两个属性不会存在 params[:comment] 里面”,还是说我遗漏了哪些细节,或者看错了官方文档。非常感谢,我是初学者。非常感谢前辈的指点。
#26 楼 @runup 文档并没有覆盖所有的情况,遇到问题需要从原理上去分析。
首先要搞清楚这个 Comment#create
action 到底是做了什么。
class CommentsController < ApplicationController
def create
@user = User.find(current_user)
@two = @user.twos.find(params[:two_id])
@comment = @two.comments.build(params[:comment]) # 主要起作用的代码行
@comment.user = @user
@comment.save
redirect_to root_path
end
end
就以这段代码为例吧,除了把 @user
@two
找出来,保存 @comment
,重定向等语句,最重要的一句,也就是真正创建那一条 @comment
的代码是
@comment = @two.comments.build(params[:comment])
那么,如果现在是要手工建立一条 @comment
,而不是在网页上提交建立的话,在 console 里面应该这样写:
@comment = @two.comments.build(:comment => "评论内容", :user => @user)
@comment
里面有三个 attribute, :comment
和 :user_id
由后面括号里面的参数提供,:two_id
由赋值等号右侧的 @two
提供,这样一来,三个 attribute 齐全,一条 @comment
就建立起来了。
对比以上两行代码,你会发现,前面的都一样,就是括号里面的参数写法不同,一个是 params[:comment]
,另一个是 :comment => "评论内容", :user => @user
但是这两行都能建立一条 @comment
,说明至少它们应该有相同的,至少是类似的格式,否则 #build
方法不认的。所以 params[:comment]
本身必须是个类似下面一行括号里面的 hash。
现在就得看看这个 params
到底是什么玩意儿了,我觉得楼主应该是卡在这里。
params
是 parameters (参数) 的缩写,在 Rails 里面,传进一个 controller action 的 params 有以下来源:
Rails 约定的参数,有 params[:controller]
和 params[:action]
等,这个先不用管。
从路由来的参数,比方你有定义 match '/user/:id'
这路由的话,访问这路径的时候就会有一个 params[:id]
的参数,值就是你实际访问的 user 的 user_id
值。
从浏览器地址栏来的参数,也就是 GET 请求传给 Rails app 的参数,比方说你在浏览器地址栏输入 http://127.0.0.1/users?user_id=15
,这就是向 Rails app 发了一个 GET 请求 ( 实际上是向 app server 发的,不过暂时不用管这个 ) ,这个 GET 请求包含了一个 params,就是 url 里面 ?
后面的那一部分,在 controller 里面可以用 params[:user_id] 访问这个参数,这个参数的值是 "15"
( string ) 。
从网页的表单来的参数,也就是 POST 请求传给 Rails app 的参数。主要跟楼主讲讲这一点。
要引发 Comment#create
方法,就要提交 app/view/twos/show.html.erb
里面的表单,发一个 POST 请求。我们看看这个表单的内容。
<%= form_for(@comment) do |f| %>
<div class="field">
<%= f.label :评论 %><br />
<%= f.text_field :comment %>
</div>
<%= hidden_field_tag(:two_id, @two.id) %> <!-- 这行是后加的 -->
<div class="actions">
<%= f.submit :提交%>
</div>
<% end %>
首先 form_for(@comment)
这定义了一个 params[:comment]
的参数,可以在接收这个表单的 action method 里面访问 ( 这个例子里面接受这个表单的 method 当然就是 Comment#create
) ,这是 Rails 的约定。
然后 do |f|
就是说,底下出现的 f
就代表这个参数了,这个 f
不一定是 f
,你用什么字符串都行,只要后面的一致就好。
再往下看, f.label
只是显示标签,不管它;下面这行 f.text_field :comment
才是有用的部分,它在网页上显示一个 text_field
也就是一个输入框,按下提交的时候,这个输入框里的内容就会跟 :comment
联系起来。怎么联系呢?上面说过 f
代表 params[:comment]
这个参数,那么 f[:comment]
表示的就是 params[:comment][:comment]
。第一个 :comment
对应 要创建的 @comment
,第二个 :comment
对应这个 @comment
里面的 comment
attribute . ( 这只能怪你名字没起好了... )
那么现在,如果把我后面加的那行蒙住不看,继续往下看,没有别的值了,直接就是提交了。也就是说这个表单提交上去的内容是什么呢?
如上面所说的,提交的是一个由 form_for(@comment)
定义的 params[:comment]
参数,这个参数是一个 hash ( Rails 的约定 ) ,如果我们假设用户在 f.text_field :comment
这个输入框里面填的是 foobar
的话,这个 hash 的值就是这样的:
params[:comment] => { :comment => "foobar" }
看到了吧,要取出 "foobar"
这个值,就要访问 params[:comment][:comment]
。
回头看 Comment#create
里面的创建 @comment
的那句关键指令:
@comment = @two.comments.build(params[:comment])
现在我们知道 params[:comment]
是什么了,把它代入上面那行,得到:
@comment = @two.comments.build(:comment => "foobar")
看到问题了没有,@comment
只有两个 attribute 得到了参数,:comment
得到了 "foobar"
,:two_id
从前面的 @two
那里得到了 id 号,但是 :user_id
是没有的。
同样道理:
@comment = @user.comments.build(params[:comment])
这句有 :user_id
了,但是缺了 :two_id
。
至于为什么我在 app/view/twos/show.html.erb
里面加了一行,在 Comment#create
里面稍作改动,就能凑齐三个 attribute 了呢?就留给楼主自己思考吧。
注意一点细节: hidden_field_tag
前面没有 f.
所以指代的参数是 params[:two_id]
而不是 params[:comment][:two_id]