Rails 文章评论添加的问题

runup · 2013年08月24日 · 最后由 runup 回复于 2013年08月30日 · 3435 次阅读

源码地址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 这个字段的值为空,说明没有传递成功。

恳请帮忙看看@sandy_xu @hisea @yorzi 非常感谢。

@huacnlee 恳请帮我看看

@happypeter 一直在看老师的视频教程,老师能不能拿帮我解答下问题~~

同学 下个断点看看@two找到了么~ 不出意外你的这个功能是评论别人的文章吧 别人的文章用自己的current_user怎么会找到@two呢...... 应该是直接Two.find(prams[:id])

@zj0713001 谢谢您的提醒。

@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 过一遍,基本就懂了。

#7 楼 @runup 呜 你没发现prams少了个 a 么...

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

@blacktulip 感激。大爱 ruby china 社区

@blacktulip 不过数据库中 two_id 还是显示为空

#14 楼 @runup

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

改这样试试行不行

#11 楼 @runup 问题很简单~ 你下个断点看看 params[:id] 是多少 目测你是 routes 没写好吧 23 没有赋值给 params[:id]

@blacktulip 还是没有效果。蔡鸟想把这个问题解决清楚

resources :users resources :twos resources :comments routes 我是这么写的,但是我之前测试过没有什么问题。

@blacktulip 非常感谢您,能够再帮我看看,刚才试了您刚才的方法之后,数据库中还是没有数据

#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 按照你的方法,暂时还没有成功,能不能再帮我看看,非常感谢。

@blacktulip 解决。感激。。。。

#23 楼 @runup 因为没有 save 呀……

@messiahxu 您好。 非常感激您的建议,我看了这篇官方文档http://guides.rubyonrails.org/association_basics.html ,并没有出现您说的这个知识点 “必须要手动指定 user_id, 或者 two_id,因为这两个属性不会存在 params[:comment] 里面”,还是说我遗漏了哪些细节,或者看错了官方文档。非常感谢,我是初学者。非常感谢前辈的指点。

#26 楼 @runup

@comment = @two.comments.new(...)

这里面新建的 @comment 会自动加入 two_id 的属性,所以你只需要手动指定 user_id 就可以了。 或者你这么看

@two = Two.find(params[:id])
@user = current_user
@comment = Comment.new(two_id: @two.id, user_id: @user.id, ...)
@comment = @two.comments.new(user_id: @user.id, ...)

上面这两个 @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 有以下来源:

  1. Rails 约定的参数,有 params[:controller]params[:action] 等,这个先不用管。

  2. 从路由来的参数,比方你有定义 match '/user/:id' 这路由的话,访问这路径的时候就会有一个 params[:id] 的参数,值就是你实际访问的 user 的 user_id 值。

  3. 从浏览器地址栏来的参数,也就是 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 ) 。

  4. 从网页的表单来的参数,也就是 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]

#28 楼 @blacktulip 出差了几天,看看看到您的回答,不知道如何表达感激了。谢谢

runup 关闭了讨论。 03月03日 10:01
runup 重新开启了讨论。 03月03日 10:01
runup 关闭了讨论。 03月03日 10:27
runup 重新开启了讨论。 03月03日 10:28
需要 登录 后方可回复, 如果你还没有账号请 注册新账号