Rails - link_to,路由和嵌套资源

kno*_*opx 27 routes ruby-on-rails nested-resources

正如我对嵌套资源的理解,边缘Rails,不应该

link_to 'User posts', @user.posts
Run Code Online (Sandbox Code Playgroud)

指向

/users/:id/posts
Run Code Online (Sandbox Code Playgroud)

routes.rb文件包含

map.resources :users, :has_many => :posts
Run Code Online (Sandbox Code Playgroud)

如果这不是默认行为,可以通过其他方式完成吗?

Rya*_*igg 54

和Rishav一样:

link_to "User Posts", [@user, :posts]
Run Code Online (Sandbox Code Playgroud)

这是我博客的解释.

真的很早就在Rails中,你会写这样的路线:

redirect_to :controller => "posts", :action => "show", :id => @post.id
Run Code Online (Sandbox Code Playgroud)

这将做的是尽职地重定向到show内部的动作,PostsController并传递id带有任何@post.id返回值的参数.典型的302响应.

然后Rails 1.2出现并允许您使用路由助手,如下所示:

redirect_to post_path(@post)
Run Code Online (Sandbox Code Playgroud)

人们欢欣鼓舞.

这将有效地做同样的事情.post_path这里将使用@post看起来类似的对象构建一个路由/posts/1,然后redirect_to向该路由发回302响应,浏览器将跟随它.

然后是更高版本(我不记得哪一个),允许这样的语法:

redirect_to @post
Run Code Online (Sandbox Code Playgroud)

人们第二次欢欣鼓舞.

魔术,但不是真的

任何足够先进的技术都与魔术无法区分.

虽然这看起来像魔术,但事实并非如此.这样做实际上非常非常整洁.该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
Run Code Online (Sandbox Code Playgroud)

如果对象不是通过调用创建的,Model.new那么它将不是新记录,如果它没有destroy调用该方法,它也不会被销毁.如果这两种情况都属实,那么这使得对象最有可能以记录的形式持久保存到数据库中.

如果它已被持久化,则url_for知道可以在某处找到此对象,并且可以找到它的位置很可能是在一个名为的方法下post_path.所以它调用这个方法,并传入to_param这个对象的值,通常是id.

简而言之,它有效地做到了这一点:

#{@post.class.downcase}_path(@post.to_param)
Run Code Online (Sandbox Code Playgroud)

这就是:

post_path(1)
Run Code Online (Sandbox Code Playgroud)

当调用该方法时,您将得到这个小字符串:

"/posts/1"
Run Code Online (Sandbox Code Playgroud)

可爱!

这称为多态路由.您可以将对象传递给类似的方法redirect_to,link_to并且form_for它将尝试计算出正确使用的URL.

form_for的形式

现在,当你编写Rails代码时,form_for很久以前就可能已经使用过了这样的代码:

<% form_for @post, :url => { :controller => "posts", :action => "create" } do |f| %>
Run Code Online (Sandbox Code Playgroud)

当然,随着Rails的进步,你可以简化它:

<% form_for @post, :url => posts_path do |f| %>
Run Code Online (Sandbox Code Playgroud)

因为表单默认使用POSTHTTP方法,因此请求posts_path将转到 create操作PostsController,而不是index操作,如果它是GET请求将导致该操作.

但为何停在那里?为什么不写这个呢?

<%= form_for @post do |f| %>
Run Code Online (Sandbox Code Playgroud)

就个人而言,我认为没有理由不......如果它像这样简单.该form_for方法使用url_for底层,就像 redirect_to找出表单应该去的地方一样.它知道@post对象属于Post类(我们再次假设),它会检查对象是否持久化.如果是,那么它将使用post_path(@post).如果不是,那么posts_path.

form_for方法本身检查传入的对象是否也是持久化的,如果是,那么它将默认为PUTHTTP方法,否则为POST.

所以这就是如何form_for灵活到在a neweditview 上都有相同的语法.如今,人们甚至将整个form_for标签整理成一个部分并将其包含在页面newedit页面中变得越来越普遍.

一种更复杂的形式

所以,form_for当你通过一个正常的对象相当简单,但如果你传递对象的数组,会发生什么?像这样,例如:

<%= form_for [@post, @comment] do |f| %>
Run Code Online (Sandbox Code Playgroud)

同时,双方url_forform_for已为你披上也在那里.

url_for方法检测到这是一个数组并分离出每个部分并单独检查它们.首先,这是什么 @post东西?那么,在这种情况下,我们假设它是Post该实例持久和有1秒的ID,这是什么 @comment目的?这是一个Comment尚未持久保存到数据库的实例.

什么url_for会在这里做的是通过将每个部分在一个阵列,其连接到路由方法,然后调用所需的参数是路由方法建立由部分网址助手方法片.

首先,它知道@post对象属于Post类并且是持久化的,因此URL助手将以其开头post.其次,它知道该@comment对象属于Comment类并且不是持久化的,因此comments将遵循postURL帮助程序构建.url_for现在知道的部分是[:post, :comments].

url_for方法将这些单独的部分与下划线组合在一起,以便它成为post_comments然后附加_path 到结尾,从而产生post_comments_path.然后它只将持久化对象传递给对该方法的调用,从而产生如下调用:

post_comments_path(@post)
Run Code Online (Sandbox Code Playgroud)

调用该方法会导致:

"/posts/1/comments"
Run Code Online (Sandbox Code Playgroud)

最好的部分?如果对象不是持久化对象,它form_for仍然会知道使用,如果是.要记住的一件好事是,始终是数组中指定的最后一个对象.它之前的对象只是它的嵌套,仅此而已.POST@commentPUTform_for

添加的对象越多,url_for执行硬码的次数就越多,并构建路径......虽然我建议您将其保留为两部分.

象征形式

现在我们已经介绍了使用包含对象的数组form_for,让我们来看看另一个常见用法.包含至少一个Symbol对象的数组,如下所示:

<%= form_for [:admin, @post, @comment] do |f| %>
Run Code Online (Sandbox Code Playgroud)

url_for方法的作用非常简单.它看到有一个Symbol并且照原样.url遗嘱的第一部分 与符号相同:admin.此时url_for知道的URL 就是[:admin].

然后url_for遍历数组的其余部分.在这种情况下,我们假设这两个@post@comment的坚持,他们分别为1和2的ID.和以前一样的课程.url_for然后添加post到它正在构建的URL comment,结果也是如此[:admin, :post, :comment].

然后连接发生,导致一个方法admin_post_comment_path,因为这两个@post并且@comment在这里持久化,它们被传入,导致这个方法调用:

admin_post_comment_path(@post, @comment)
Run Code Online (Sandbox Code Playgroud)

哪(通常)变成这条路径:

/admin/posts/1/comments/2
Run Code Online (Sandbox Code Playgroud)

您可以使用多态路由的阵列形式redirect_to,link_toform_for方法.可能还有其他我现在也不记得的方法也可以做到这一点......通常Rails中的任何方法通常都会使用URL.

没有必要使用哈希在任何大于2的Rails版本中构建您的URL; 那是一所非常古老的学校.

相反,尝试一下您对多态路由的新知识,并尽可能地利用它.


Ris*_*ogi 16

这应该工作:

 
 link_to "User Posts", user_posts_path(@user)

更多详情,请访问:

http://guides.rubyonrails.org/routing.html