据我理解,嵌套资源在边缘版的Rails中不应该出现
link_to 'User posts', @user.posts
指向/users/:id/posts
routes.rb 文件包含路由信息
map.resources :users, :has_many => :posts
如果这不是默认行为,那么可以通过其他方式实现吗?
据我理解,嵌套资源在边缘版的Rails中不应该出现
link_to 'User posts', @user.posts
指向/users/:id/posts
routes.rb 文件包含路由信息
map.resources :users, :has_many => :posts
如果这不是默认行为,那么可以通过其他方式实现吗?
在Rishav的思路基础上:
link_to "User Posts", [@user, :posts]
以下是我在我的博客中的解释。
在 Rails 早期,你会这样编写路由:
redirect_to :controller => "posts", :action => "show", :id => @post.id
这将会有效地重定向到PostsController
内的show
动作,并将id
参数与@post.id
返回的任何值一起传递。通常会得到302响应。
然后,Rails 1.2引入了路由辅助函数,使您可以像这样使用:
redirect_to post_path(@post)
人们欣喜若狂。
这将有效地完成相同的操作。这里的post_path
将使用@post
对象构建路由,类似于/posts/1
,然后redirect_to
会向该路由发送302响应,浏览器会跟随它。
后来的版本(我记不得是哪个版本了)允许像这样使用语法:
redirect_to @post
人们第二次欢呼雀跃。
任何足够先进的技术都无法与魔法区分开来。
虽然这看起来像魔法,但实际上不是。它所做的事情非常巧妙。像 redirect_to
方法一样,它的兄弟方法 link_to
和 form_for
都使用一个通用方法来构建URL,叫做 url_for
。 url_for
方法接受许多不同类型的对象,例如字符串、哈希表甚至是模型的实例,就像上面的例子那样。
然后它对这些对象做了很多事情。对于上面的 redirect_to @post
调用,它检查 @post
对象,看到它是 Post
类的对象(我们假设的),并检查是否调用 persisted?
方法在某个数据库中保留了该对象。
“保留”意味着 Ruby 对象在数据库中有一个匹配的记录。在Active Record中,persisted?
方法的实现如下:
def persisted?
!(new_record? || destroyed?)
end
如果对象不是通过如Model.new
的调用创建的,则不会是一个新记录,如果它没有调用destroy
方法,它也不会被销毁。如果这两种情况都成立,那么该对象很可能以记录的形式被保存到数据库中。url_for
知道可以在哪里找到此对象,而且最有可能在一个名为post_path
的方法下找到它。因此,它调用此方法,并传递此对象的to_param
值,通常是id
。#{@post.class.downcase}_path(@post.to_param)
这就是最终结果:
post_path(1)
当调用该方法时,您将获得此小字符串:
"/posts/1"
太好了!
这被称为多态路由(polymorphic routing)。你可以将一个对象传递给像redirect_to
、link_to
和form_for
这样的方法,它会试图找出正确的URL以使用。
form_for
的形式现在,当你编写Rails时,你可能很久以前就像这样使用过form_for
:
<% form_for @post, :url => { :controller => "posts", :action => "create" } do |f| %>
当然,随着Rails技术的进步,你可以将其简化为:
<% form_for @post, :url => posts_path do |f| %>
因为表单默认使用POST
HTTP方法,所以对posts_path
的请求将进入PostsController
的create
操作,而不是GET
请求下的index
操作。<%= form_for @post do |f| %>
就我个人而言,如果这只是一些简单的东西,我认为没有理由不……使用。form_for
方法在底层使用url_for
,就像redirect_to
一样,确定表单应该去哪里。它知道@post
对象是Post
类(我们再次假设),并检查对象是否持久化。如果是,则使用post_path(@post)
。如果不是,则使用posts_path
。
form_for
方法本身也会检查传入的对象是否持久化,如果是,则默认使用PUT
HTTP方法,否则使用POST
。
因此,这就是为什么form_for
足够灵活,以在new
和edit
视图上具有相同的语法。现在越来越普遍的是,人们甚至将整个form_for
标记放入单个部分中,并将其包含在new
和edit
页面中。
对于传递普通对象的情况,form_for
相当简单,但如果您传递了对象数组会发生什么?例如:
<%= form_for [@post, @comment] do |f| %>
url_for
方法会将数组中的每个部分分离出来并逐个检查。在这种情况下,@post
是一个已经存在于数据库中,id为1的Post
实例,@comment
是一个尚未存在于数据库中的Comment
实例。url_for
方法会将这两部分连接起来,形成URL,并返回相应的路由方法。调用该路由方法时,仅需传入已经存在于数据库中的对象即可。post_comments_path(@post)
调用该方法会导致以下结果:
"/posts/1/comments"
最好的部分?form_for
将根据@comment
对象是否为持久化对象,自动选择使用POST
或PUT
。需要记住的一件好事是,form_for
始终是针对数组中指定的最后一个对象。其前面的对象只是嵌套它的,没有其他作用。
添加的对象越多,url_for
将会越多地执行艰苦的工作并构建路径...尽管我建议您将其保留为仅两个部分。
既然我们已经介绍了使用包含对象的数组来使用form_for
,现在让我们再看看另一个常见用法。包含至少一个符号对象的数组,如下所示:
<%= form_for [:admin, @post, @comment] do |f| %>
url_for
方法在这里的作用非常简单。它看到有一个Symbol
并将其直接使用。URL的第一部分将只是与该标志相同:admin
。此时url_for
所知道的URL仅为[:admin]
。url_for
遍历数组的其余部分。在这种情况下,假设@post
和@comment
都已持久化,并且它们的ID分别为1和2。与之前相同的类。url_for
然后将post
添加到正在构建的URL中,也将comment
添加到其中,从而产生了[:admin, :post, :comment]
。admin_post_comment_path
方法,并且因为这里都已经持久化了@post
和@comment
,它们被传递进去,形成了这个方法调用:admin_post_comment_path(@post, @comment)
通常会转换成这个路径:
/admin/posts/1/comments/2
您可以使用redirect_to
、link_to
和form_for
方法中的多态路由数组形式。可能还有其他方法我现在没有想起来,通常是Rails中任何通常需要URL的东西都可以。在任何大于2的Rails版本中,都不需要使用哈希构建URL,那太老派了。
相反,尝试利用您新学到的多态路由知识并充分利用它。
这应该可以工作:
link_to "用户帖子", user_posts_path(@user)
要了解更多详情,请访问:
link_to 'Destroy Comment', post_comment_path(comment)
(在 _partial 中)也不起作用。 - Chloelink_to
使用url_for
,而polymorphic_url
是url_for
所使用的方法之一。
polymorphic_url
:
因此,像其他人所说的那样,您应该使用:
link_to 'User Posts', [@user, :posts]
路径为:
user_posts_path(@user)
^^^^ ^^^^^ ^^^^^
1 2 3
@user
的类这样就构建了一个好的辅助方法。
注意:这是在一个局部视图文件中,所以没有使用link_to '删除评论', post_comment_path(comment.post, comment)
@
。
[comment.post, comment]
可以正确生成你所要求的post_comment_path
辅助函数。我甚至写了一篇文章介绍这个问题:http://ryanbigg.com/2012/03/polymorphic-routes/ - Ryan Biggpost_comment_path
方法是错误的。你应该先传递 post 对象,然后再传递 comment。 - Ryan Bigglink_to 'Destroy Comment', post_comment_path(comment.post, comment)
来生成http://127.0.0.1:3000/posts/1/comments/2
,其中http://127.0.0.1:3000/posts/1
是帖子链接。 - Chloeurl_for([:new, @post, :comment])
,因为您还没有嵌套对象;这将生成正确的路径! - carlosayam