Rails – link_to,路由和嵌套资源
由于我对嵌套资源的理解,在边缘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对象在某个地方的数据库中有匹配的记录。 persisted?
Active Record中的方法实现如下:
def persisted? !(new_record? || destroyed?) end
如果对象不是通过诸如Model.new
的调用创建的,那么它将不是新记录,如果它没有调用的destroy
方法,它也不会被销毁。 如果这两种情况都属实,那么这使得对象很可能以记录的forms持久保存到数据库中。
如果它已被持久化,则url_for
知道可以在某处找到此对象,并且可以找到它的位置很可能是在名为post_path
的方法下。 所以它调用此方法,并传入此对象的to_param
值,该值通常是id
。
简而言之,它实际上是这样做的:
#{@post.class.downcase}_path(@post.to_param)
这就是这样的:
post_path(1)
当调用该方法时,您将得到这个小字符串:
"/posts/1"
可爱!
这称为多态路由 。 您可以将对象传递给redirect_to
, link_to
和form_for
等方法,它会尝试找出使用内容的正确URL。
form_for的forms
现在,当你编写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
的请求将posts_path
的create
操作,而不是index
操作,如果它是GET
请求,则会导致该操作。
但为什么要停在那里? 为什么不写这个呢?
<%= 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
页面中变得越来越普遍。
一种更复杂的forms
因此,当您传递普通对象时, form_for
非常简单,但是如果传递一个对象数组会发生什么? 像这样,例如:
<%= form_for [@post, @comment] do |f| %>
好吧, url_for
和form_for
都让你也在那里。
url_for
方法检测到这是一个数组并分离出每个部分并单独检查它们。 首先,这个@post
是什么东西? 好吧,在这种情况下,我们假设它是一个持久化的Post
实例,其id为1.其次,这个@comment
对象是什么? 它是一个尚未持久保存到数据库的Comment
实例。
url_for
将在这里做的是通过将每个部分放在一个数组中,将它连接到一个路由方法,然后使用必要的参数调用该路由方法,逐个构建URL帮助器方法。
首先,它知道@post
对象属于Post
类并且是持久的,因此URL助手将以post
开头。 其次,它知道@comment
对象是Comment
类并且没有持久化,因此comments
将在URL帮助程序构建中跟随post
。 url_for
现在知道的部分是[:post, :comments]
。
url_for
方法将这些单独的部分与下划线组合在一起,以便它成为post_comments
,然后将_path
附加到其末尾,从而生成post_comments_path
。 然后它只将持久化对象传递给对该方法的调用,从而产生如下调用:
post_comments_path(@post)
调用该方法会导致:
"/posts/1/comments"
最好的部分? 如果@comment
对象不是持久对象, form_for
仍然会知道使用POST
如果是,则仍然是PUT
。 要记住的一件好事是, form_for
总是用于数组中指定的最后一个对象。 之前的对象只是它的嵌套,仅此而已。
添加的对象越多, url_for
执行硬码并构建路径的次数就越多……虽然我建议您将其保留为两部分。
象征forms
现在我们已经介绍了使用包含form_for
对象的数组,让我们来看看另一个常见用法。 包含至少一个Symbol对象的数组,如下所示:
<%= 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
方法的多态路由的数组forms。 可能还有其他我现在也不记得的方法也可以做到这一点……通常Rails中的任何方法通常都会使用URL。
没有必要使用哈希在任何大于2的Rails版本中构建您的URL; 那是一所非常古老的学校。
相反,尝试一下您对多态路由的新知识,并尽可能地利用它。
link_to
使用url_for
,它使用polymorphic_url
。
polymorphic_url
:
-
使用活动记录对象的类名构建帮助器方法
-
使用活动记录对象作为参数调用帮助程序
因此,正如其他人所说,你应该使用:
link_to 'User Posts', [@user, :posts]
路径是:
user_posts_path(@user) ^^^^ ^^^^^ ^^^^^ 1 2 3
-
@user
类,因为它是一个活动记录 - 转换为字符串,因为符号
- 添加为调用参数,因为活动记录
这构建了良好的帮助方法。
这是如何链接到最新Rails中的嵌套资源:
link_to’Destroy Comment’,post_comment_path(comment.post,comment)
注意:这是部分所以没有@
。