Rails - link_to, маршруты и вложенные ресурсы - PullRequest
27 голосов
/ 10 октября 2009

Как я понимаю, на вложенных ресурсах, на краю Rails, не должно

link_to 'User posts', @user.posts

указывает на

/users/:id/posts

Файлways.rb содержит

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

Если это не стандартное поведение, можно ли выполнить что-то еще?

Ответы [ 4 ]

54 голосов
/ 11 октября 2009

В том же духе, что и Ришав:

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)

И люди радовались.

Это фактически сделало бы то же самое. 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, он не будет уничтожен либо. Если оба эти случая верны, то это, скорее всего, означает, что объект был сохранен в базе данных в виде записи.

Если он был сохранен, то 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

Теперь, когда вы кодируете Rails, вы могли использовать form_for как это очень давно:

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

Конечно, с улучшениями в Rails вы можете упростить это до:

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

Поскольку форма по умолчанию использует HTTP-метод POST и, следовательно, запрос к 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 в один частичный и включают его в new и edit стр.

Более сложная форма

Итак, form_for довольно просто, когда вы передаете нормальный объект, но что произойдет, если вы передадите массив объектов? Как это, для пример:

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

Ну, и url_for, и form_for ты тоже там прикрыл.

Метод url_for обнаруживает, что это массив, выделяет каждую часть и проверяет их индивидуально. Во-первых, что это @post вещь? Хорошо, в этом случае давайте предположим, что это Post экземпляр, который сохраняется и имеет идентификатор 1. Во-вторых, что это @comment объект? Это экземпляр Comment, который еще не был сохранен в базе данных.

Что будет делать url_for, так это построение метода-помощника URL по частям путем помещения каждой части в массив, объединения ее в метод маршрутизации и последующего вызова этого метода маршрутизации с необходимыми аргументами.

Во-первых, он знает, что объект @post относится к классу Post и сохраняется, поэтому помощник URL будет начинаться с post. Во-вторых, он знает, что объект @comment относится к классу Comment и не сохраняется, и поэтому comments будет следовать post в сборке помощника URL. Части, о которых url_for теперь знает, это [:post, :comments].

Метод url_for объединяет эти отдельные части с подчеркиванием, так что он становится post_comments, а затем добавляет _path до конца этого, в результате post_comments_path. Затем он передает только сохраненные объекты для вызова этого метода, что приводит к такому вызову:

post_comments_path(@post)

Вызов этого метода приводит к следующему:

"/posts/1/comments"

Лучшая часть? form_for по-прежнему будет знать, использовать POST, если объект @comment не является постоянным объектом, и PUT, если это так. Хороший Следует помнить, что form_for всегда для последнего объекта, указанного в массиве. Объекты до него - это просто его вложение, ничего более.

Чем больше объектов будет добавлено, тем больше раз url_for будет проходить трудные дворы и прокладывать путь ... хотя я рекомендую Вы держите это только на две части.

Символическая форма

Теперь, когда мы рассмотрели использование массива, содержащего объекты для form_for, давайте рассмотрим другое распространенное использование. Массив, содержащий хотя бы один объект Symbol, например:

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

То, что делает здесь метод url_for, очень просто. Он видит, что есть Symbol, и принимает его как есть. Первая часть url будет просто совпадать с символом: admin. URL, который url_for знает на данный момент, это просто [:admin].

Затем url_for проходит через остальные части массива. В этом случае предположим, что и @post, и @comment сохраняются и что они имеют идентификаторы 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. Там, вероятно, другие методы, которые я сейчас не помню, которые тоже могут это сделать ... это вообще что-то в Rails, которое обычно принимает URL.

Нет необходимости создавать ваши URL-адреса в любой версии Rails больше 2, используя хэши; это довольно старая школа.

Вместо этого поэкспериментируйте с новыми знаниями о полиморфной маршрутизации и используйте их в своих интересах.

16 голосов
/ 10 октября 2009

Это должно работать:

 
 link_to "User Posts", user_posts_path(@user)

для более подробной информации посетите:

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

2 голосов

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. добавить в качестве аргумента вызова, поскольку активная запись

Это строит хороший вспомогательный метод.

1 голос
/ 08 октября 2013

Вот как сделать ссылку на вложенный ресурс в последних Rails:

link_to 'Уничтожить комментарий', post_comment_path (comment.post, комментарий)

Примечание: это частично, поэтому @.

...