Rails-link_to,路线和嵌套资源 [英] Rails - link_to, routes and nested resources

查看:74
本文介绍了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 

此操作将忠实地重定向到 show 动作在 PostsController 内,并传递 id 参数,且
的值为 @ post.id 返回的值。典型的302响应。



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

  redirect_to post_path(@post)

人民很高兴。 / p>

这实际上可以做同样的事情。 post_path 此处将使用 @post 对象构建一条路线,该路线看起来像
,如 / posts / 1 ,然后 redirect_to 将向该路由发送回302响应,浏览器将遵循该路由。



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

  redirect_to @post 

人民再次欢喜。



魔术,但不是真的




任何足够先进的技术都不能与魔术区分开。


虽然这看起来像魔术,但并非如此。这实际上是非常非常整洁的。 redirect_to 方法,非常类似于其堂兄 link_to form_for 所有人都使用称为 url_for 的通用方法来构建URL。 url_for 方法采用许多不同的
对象,例如字符串,哈希或什至是模型实例,例如上面的示例。



那么这些对象的作用就很整洁。对于上面的 redirect_to @post 调用,它检查 @post
对象,发现它是 Post 类的一个对象(无论如何,我们假设),并通过调用<$ c来检查该对象是否已保存在
数据库中的某个位置$ c>坚持了吗?。



持久是指Ruby对象在数据库中某处具有匹配的记录。 Active Record中的 persisted?方法是这样实现的:

  def坚持吗? 
!(new_record?||已销毁?)
end

如果对象不是通过 Model.new 之类的调用创建的,那么它就不会是新记录,并且如果没有 destroy 调用的方法也不会被
销毁。如果这两种情况都成立,则使得该对象很可能以记录的形式持久化到数据库中。



如果已保留,则 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


$ b的形式$ b

现在,当您为Rails编码时,您可能很久以前就使用过 form_for

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

当然,随着Rails的改进,您可以将其简化为:

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

因为该表单将默认使用 POST HTTP方法,因此对 posts_path 的请求将转到
create 操作 PostsController ,而不是 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 标记放到一个部分中并将其包括在中,这变得越来越普遍。新
编辑页。



更复杂的表单



因此 form_for 对于传递普通对象非常简单,但是如果传递对象数组会发生什么呢?像这样,对于
实例:

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

好吧, url_for form_for 您也覆盖了那里。



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



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



首先,它知道 @post 对象属于 Post 类并保持不变,因此URL帮助器将以 post 开头。其次,它知道 @comment 对象属于 Comment 类,并且是 not 保持不变,因此注释将遵循URL帮助程序构建中的 post url_for 现在知道的部分是 [:post,:comments]



url_for 方法将这些各个部分与下划线组合在一起,因此它成为 post_comments 和然后在末尾附加 _path
,得到 post_comments_path 。然后,它仅将持久对象传递给该方法的调用,从而导致如下调用:

  post_comments_path(@post )

调用该方法将导致:

  /帖子/ 1 /评论 

最好的部分?如果 @评论 form_for 仍会知道使用 POST object不是持久对象,如果是 PUT ,则不是持久对象。
要记住的一件好事是, form_for 始终用于数组中指定的 last 对象。



添加的对象越多,其次数就越多。 url_for 会做些艰难的尝试并建立一条路径……尽管我建议您将
保留为两个部分。



符号形式



现在,我们已经使用包含对象的数组覆盖了 form_for ,让我们看一下另一个常见用途。包含
至少一个Symbol对象的数组,如下所示:

 <%= form_for [:admin,@帖子,@ comment]做| f | %> 

url_for 方法的作用非常简单。它看到有一个 Symbol 并按原样使用。
url 的第一部分将与符号相同: admin 。此时 url_for 知道的URL只是 [:admin]



然后 url_for 遍历数组的其余部分。在这种情况下,我们假设 @post @comment 都是持久的
并且它们都具有ID分别为1和2。与以前相同的课程。 url_for 然后将 post 添加到其正在构建的URL,
注释,也导致 [:admin,:post,:comment]



然后发生连接,导致方法 admin_post_comment_path ,并且由于 @post @注释保留在此处,传递给他们
,导致此方法调用:

  admin_post_comment_path(@post,@comment)

(通常)变成以下路径:

  / admin / posts / 1 / comments / 2 






您可以将多态路由的数组形式与 redirect_to link_to form_for 方法。我可能现在还不记得其他
个方法也可以做到...通常在Rails中通常使用URL的任何东西。



无需使用散列在大于2的任何Rails版本中构建URL;



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


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?

解决方案

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.

这篇关于Rails-link_to,路线和嵌套资源的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆