Подтвердить что ты не робот

Rails - link_to, маршруты и вложенные ресурсы

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

link_to 'User posts', @user.posts

укажите

/users/:id/posts

?

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

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

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

4b9b3361

Ответ 1

По тем же линиям, что и 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)

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

Это будет делать то же самое. 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 object, видит, что это объект класса Post (мы предполагаем, в любом случае), и проверяет, сохраняется ли этот объект в где-то, позвонив persisted?.

Под "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| %>

Поскольку форма по умолчанию имеет метод 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 метод, в противном случае a 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 с использованием хэшей; эта довольно старая школа.

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

Ответ 2

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

 
 link_to "User Posts", user_posts_path(@user)

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

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

Ответ 3

Это ссылка на вложенный ресурс в последнем Rails:

link_to 'Destroy Comment', post_comment_path (комментарий .post, комментарий)

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

Ответ 4

link_to использует url_for, который использует polymorphic_url.

polymorphic_url:

Поэтому, как говорили другие, вы должны использовать:

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

для которого путь:

user_posts_path(@user)
^^^^ ^^^^^      ^^^^^
1    2          3
  • класс @user, потому что это активная запись
  • конвертировать в строку, потому что символ
  • добавить в качестве аргумента вызова, поскольку активная запись

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