【问题标题】:Rails - link_to, routes and nested resourcesRails - link_to、路由和嵌套资源
【发布时间】:2009-10-10 13:49:23
【问题描述】:

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

link_to 'User posts', @user.posts

指向

/users/:id/posts

?

routes.rb 文件包含

map.resources :users, :has_many => :posts

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

【问题讨论】:

    标签: ruby-on-rails routes nested-resources


    【解决方案1】:

    与 Rishav 的思路相同:

    link_to "User Posts", [@user, :posts]
    

    这是来自my blog的解释。

    在 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_toform_for 一样,都使用一种通用的方法来构建 URL,称为 url_forurl_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"
    

    可爱!

    这称为多态路由。您可以将对象传递给 redirect_tolink_toform_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 的请求将转到 createPostsController 的操作,而不是 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 足够灵活以在newedit 视图上具有相同语法的方式。它变得越来越 如今,人们甚至将整个 form_for 标签放在一个部分中并将其包含在 newedit 页。

    更复杂的形式

    所以form_for 在传递普通对象时相当简单,但如果传递对象数组会发生什么?像这样,对于 实例:

    <%= form_for [@post, @comment] do |f| %>
    

    嗯,url_forform_for 你也有报道。

    url_for 方法检测到这是一个数组并分离出每个部分并单独检查它们。首先,这是什么 @post 东西?好吧,在这种情况下,我们假设它是一个 Post 实例,is 持续存在并且 id 为 1。其次,这是什么 @comment 对象?这是一个尚未持久化到数据库的Comment 实例。

    url_for 将在这里做的是通过将每个部分放在一个数组中,将其连接到一个路由方法中,然后使用必要的参数调用该路由方法,从而逐步构建 URL 帮助器方法。

    首先,它知道@post 对象属于Post 类并且是持久的,因此URL 助手将以post 开头。其次,它知道@comment 对象属于Comment 类并且 持久化,因此comments 将在URL 帮助程序构建中跟随posturl_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 始终用于数组中指定的 last 对象。在它之前的对象只是它的 嵌套,仅此而已。

    添加的对象越多,url_for 会越多地完成硬码并构建路径......虽然我建议 你只保留两部分。

    符号形式

    现在我们已经介绍了使用包含 form_for 对象的数组,让我们看看另一个常见的用法。一个数组包含 至少一个 Symbol 对象,像这样:

    <%= form_for [:admin, @post, @comment] do |f| %>
    

    url_for 方法在这里所做的非常简单。它看到有一个Symbol 并按原样接受。第一部分 url 将与符号相同:adminurl_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_tolink_toform_for 方法一起使用。应该还有别的 我现在不记得的方法也可以做到这一点……通常是 Rails 中通常需要 URL 的任何东西。

    没有必要在任何大于 2 的 Rails 版本中使用哈希构建 URL;那是相当古老的学校。

    相反,尝试使用您对多态路由的新知识并充分利用它。

    【讨论】:

    • 这在最新的 Rails 中似乎无效。没有接受数组的方法签名。 api.rubyonrails.org/classes/ActionView/Helpers/…
    • @Chloe 我很确定这在最新版本的 Rails 中仍然有效。请在对我投反对票之前尝试一下。
    • @Chloe:API 文档有时无法反映现实。我做 Rails 的时间已经够久了,我知道 [comment.post, comment] 会正确生成您要求的 post_comment_path 助手。我什至写了一篇关于这个的帖子:ryanbigg.com/2012/03/polymorphic-routes
    • 你的 post_comment_path 方法是错误的,顺便说一句。您应该首先传递 post 对象,然后是评论。
    • 是的,你是对的。应该是link_to 'Destroy Comment', post_comment_path(comment.post, comment) 为帖子http://127.0.0.1:3000/posts/1 生成http://127.0.0.1:3000/posts/1/comments/2
    【解决方案2】:

    这应该可行:

    link_to "用户帖子", user_posts_path(@user)

    更多详情请访问:

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

    【讨论】:

    • 这对link_to 'Destroy Comment', post_comment_path(comment) 也不起作用(在_partial 内)。
    【解决方案3】:

    link_to 使用 url_for,后者使用 polymorphic_url

    polymorphic_url:

    因此,正如其他人所说,您应该使用:

    link_to 'User Posts', [@user, :posts]
    

    路径是:

    user_posts_path(@user)
    ^^^^ ^^^^^      ^^^^^
    1    2          3
    
    1. @user 的类,因为它是活动记录
    2. 因为符号转换为字符串
    3. 添加为调用参数,因为活动记录

    这构建了良好的辅助方法。

    【讨论】:

      【解决方案4】:

      这是在最新的 Rails 中链接到嵌套资源的方法:

      link_to '销毁评论', post_comment_path(comment.post, comment)

      注意:这是部分内容,因此没有@

      【讨论】:

        猜你喜欢
        • 2011-07-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-04-05
        • 2011-05-23
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多