Rails - link_to、路线和嵌套资源

发布于 2024-08-06 20:18:27 字数 286 浏览 11 评论 0原文

根据我对边缘 Rails 上嵌套资源的理解,不应该

link_to 'User posts', @user.posts

指向

/users/:id/posts

routes.rb 文件包含

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

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

As my understanding on nested resources, on edge Rails, should not

link_to 'User posts', @user.posts

point to

/users/:id/posts

?

The routes.rb file contains

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

If this is not the default behavior, can it be accomplished doing something else?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

著墨染雨君画夕 2024-08-13 20:18:27

与 Rishav 一样:

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

这是来自我的博客的解释。

在 Rails 的早期,您可以这样编写路由:

redirect_to :controller => "posts", :action => "show", :id => @post.id

这将尽职尽责地重定向到 PostsController 内的 show 操作,并传递 id< /code> 参数带有
@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 的请求将转到
PostsControllercreate 操作,而不是 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 标记放入单个部分中,并将其包含在 new
编辑页面。

更复杂的形式

因此,当您传递普通对象时 form_for 相当简单,但是如果您传递对象数组会发生什么?像这样,为了
例如:

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

嗯,url_forform_for 也都涵盖了这一点。

url_for 方法检测到这是一个数组,并分离出每个部分并单独检查它们。首先,这是什么
@post 东西?好吧,在这种情况下,我们假设它是一个持久化的 Post 实例,并且 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 将与符号相同: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_tolink_toform_for 方法。应该还有其他的
我现在不记得的方法也可以做到这一点...通常是 Rails 中通常需要 URL 的任何内容。

无需使用哈希在任何大于 2 的 Rails 版本中构建 URL;那是相当老派的。

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

Along the same lines as Rishav:

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

Here's an explanation from my blog.

Really early on in Rails, you would write routes like this:

redirect_to :controller => "posts", :action => "show", :id => @post.id

What this would do is dutifully redirect to the show action inside the PostsController and pass along the id parameter with a
value of whatever @post.id returns. Typical 302 response.

Then Rails 1.2 came along and allowed you to use routing helpers, like this:

redirect_to post_path(@post)

And the people rejoiced.

This would do effectively the same thing. post_path here would build a route using the @post object that would look something
like /posts/1 and then redirect_to would send back a 302 response to that route and the browser would follow it.

Then later versions (I can't remember which one), allowed syntax like this:

redirect_to @post

And the people rejoiced a second time.

Magic, but not really

Any sufficiently advanced technology is indistinguishable from magic.

While this seems like magic, it's not. What this is doing is actually very, very neat. The redirect_to method, much like its cousins link_to and form_for all use a common method to build URLs, called url_for. The url_for method takes many different
varieties of objects, such as strings, hashes or even instances of models, like in the example above.

What it does with these objects then, is quite neat. In the case of the redirect_to @post call above, it inspects the @post
object, sees that it is an object of the Post class (we assume, anyway) and checks to see if that object has been persisted in a
database somewhere by calling persisted? on it.

By "persisted", I mean that a Ruby object has a matching record in the database somewhere. The persisted? method in Active Record is implemented like this:

def persisted?
  !(new_record? || destroyed?)
end

If the object wasn't created through a call such as Model.new then it won't be a new record, and if it hasn't had the destroy method called on it won't be
destroyed either. If both of these cases are true, then that makes the object has most likely been persisted to the database in the form of a record.

If it has been persisted, then url_for knows that this object can be found
somewhere, and that the place it can be found is most likely under a method called post_path. So it calls this method, and passes
in the to_param value of this object which is usually the id.

In short, it's effectively doing this:

#{@post.class.downcase}_path(@post.to_param)

Which comes out to being this:

post_path(1)

And when that method is called you would get this little string:

"/posts/1"

Lovely!

This is called polymorphic routing. You can pass an object to methods like redirect_to, link_to and form_for and it will
attempt to work out the correct URL of what to use.

The form of form_for

Now, when you're coding Rails you may have used form_for like this a very long time ago:

<% form_for @post, :url => { :controller => "posts", :action => "create" } do |f| %>

Of course, with advancements in Rails you could simplify it to this:

<% form_for @post, :url => posts_path do |f| %>

Because the form is going to default to having a POST HTTP method and therefore a request to posts_path is going to go to the
create action of PostsController, rather than the index action, which is what would result if it were a GET request.

But why stop there? Why not just write this?

<%= form_for @post do |f| %>

Personally, I see no reason not to... if it's something as simple as this. The form_for method uses url_for underneath, just like
redirect_to to work out where the form should go. It knows that the @post object is of the Post class (again, we assume) and it
checks to see if the object is persisted. If it is, then it will use post_path(@post). If it's not, then posts_path.

The form_for method itself checks to see if the object passed in is persisted also, and if it is then it'll default to a PUT HTTP
method, otherwise a POST.

So this is how form_for can be flexible enough to have an identical syntax on both a new and edit view. It's becoming more and
more common these days for people to even put their whole form_for tags into a single partial and include it in both the new and
edit pages.

A more complex form

So form_for is fairly simple for when you pass a normal object, but what happens if you pass an array of objects? Like this, for
instance:

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

Well, both url_for and form_for have you covered there too.

The url_for method detects that this is an array and separates out each part and inspects them individually. First, what is this
@post thing? Well, in this case let's assume it's a Post instance that is persisted and has the id of 1. Second, what is this
@comment object? It's a Comment instance that has not yet been persisted to the database.

What url_for will do here is build up the URL helper method piece by piece by placing each part in an array, joining it into a routing method and then calling that routing method with the necessary arguments.

First, it knows that the @post object is of the Post class and is persisted, therefore the URL helper will begin with post. Second, it knows that the @comment object is of the Comment class and is not persisted, and therefore comments will follow post in the URL helper build. The parts that url_for now knows about are [:post, :comments].

The url_for method combines these individual parts with an underscore, so that it becomes post_comments and then appends _path
to the end of that, resulting in post_comments_path. Then it passes in just the persisted objects to the call to that method, resulting in a call like this:

post_comments_path(@post)

Calling that method results in this:

"/posts/1/comments"

Best part? form_for will still know to use POST if the @comment object is not a persisted object, and PUT if it is. A good
thing to remember is that the form_for is always for the last object specified in the array. The objects prior to it are just its
nesting, nothing more.

The more objects that are added, the more times url_for will do the hard yards and build the path out... although I recommend that
you keep it to just two parts.

A symbolic form

Now that we've covered using an array containing objects for form_for, let's take a look at another common use. An array containing
at least one Symbol object, like this:

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

What the url_for method does here is very simple. It sees that there's a Symbol and takes it as it is. The first part of the
url will simply be the same as the symbol: admin. The URL that url_for knows of at this point is just [:admin].

Then url_for goes through the remaining parts of the array. In this case, let's assume both @post and @comment are persisted
and that they have the ids of 1 and 2 respectively. Same classes as before. url_for then adds post to the URL that it's building,
and comment too, resulting in [:admin, :post, :comment].

Then the joining happens, resulting in a method of admin_post_comment_path, and because both @post and @comment are persisted here,
they're passed in, resulting in this method call:

admin_post_comment_path(@post, @comment)

Which (usually) turns into this path:

/admin/posts/1/comments/2

You can use the array form of polymorphic routing with the redirect_to, link_to and form_for methods. There's probably other
methods that I'm not remembering right now that can do it too... it's generally anything in Rails that would normally take a URL.

There's no need to build your URLs in any Rails version greater-than 2 using hashes; that's pretty old school.

Instead, experiment with your new knowledge of polymorphic routing and use it to the best of your advantage.

怪异←思 2024-08-13 20:18:27

这应该有效:

 
 link_to "User Posts", user_posts_path(@user)

有关更多详细信息,请访问:

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

This should work:

 
 link_to "User Posts", user_posts_path(@user)

for more details visit:

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

飘落散花 2024-08-13 20:18:27

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. 添加为调用参数 因为活动记录

这构建了良好的帮助器方法。

link_to uses url_for which uses polymorphic_url.

polymorphic_url:

Therefore, as others said, you should use:

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

for which the path is:

user_posts_path(@user)
^^^^ ^^^^^      ^^^^^
1    2          3
  1. class of @user because it is an active record
  2. convert to string because symbol
  3. add as call argument because active record

That builds the good helper method.

十年九夏 2024-08-13 20:18:27

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

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

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

This is how to link to a nested resource in the latest Rails:

link_to 'Destroy Comment', post_comment_path(comment.post, comment)

Note: This is in a partial so there isn't a @.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文