форма для вложенных ресурсов


у меня есть вопрос из двух частей о form_for и вложенных ресурсах. Допустим, я пишу блог-движок и хочу связать комментарий со статьей. Я определил вложенный ресурс следующим образом:

map.resources :articles do |articles|
    articles.resources :comments
end

форма комментария находится в шоу.формат html.erb view для статей, под самой статьей, например, так:

<%= render :partial => "articles/article" %>
<% form_for([ :article, @comment]) do |f| %>
    <%= f.text_area :text %>
    <%= submit_tag "Submit" %>
<%  end %>

Это дает ошибку, " называется id для nil, который ошибочно и т. д."Я тоже пробовал

<% form_for @article, @comment do |f| %>

что делает правильно, но связывает f. text_area с полем "текст" статьи Вместо комментария и представляет html для статьи.текстовый атрибут в этой текстовой области. Так что я, кажется, тоже ошибаюсь. То, что я хочу, - это форма, в которой "submit" вызовет действие create на CommentsController, с article_id в параметрах, например, запрос post на /articles/1/comments.

вторая часть моего вопроса заключается в том, что это лучший способ создать экземпляр комментария для начала? Я создание @comment в действии show элемента управления ArticlesController, поэтому объект comment будет находиться в области действия помощника form_for. Затем в действии create CommentsController я создаю новый @comment, используя параметры, переданные из form_for.

спасибо!

3 109

3 ответа:

Трэвис Р прав. (Жаль, что я не могу выдвинуть тебя.) Я только что получил это работает сам. С помощью этих маршрутов:

resources :articles do
  resources :comments
end

вы получаете пути, как:

/articles/42
/articles/42/comments/99

направляется на контроллеры в

app/controllers/articles_controller.rb
app/controllers/comments_controller.rb

так же, как он говорит в http://guides.rubyonrails.org/routing.html#nested-resources, без специальных пространств имен.

но частичные и формы становятся хитрыми. Обратите внимание на квадратные скобки:

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

самое главное, если вы хотите URI, вам может понадобиться что-то вроде этого:

article_comment_path(@article, @comment)

кроме того:

[@article, @comment]

как описано на http://edgeguides.rubyonrails.org/routing.html#creating-paths-and-urls-from-objects

например, внутри коллекции частично с comment_item поставить для итерации,

<%= link_to "delete", article_comment_path(@article, comment_item),
      :method => :delete, :confirm => "Really?" %>

то, что говорит джамураа, может работать в контексте статьи, но это не сработало для меня в различных других пути.

существует много дискуссий, связанных с вложенными ресурсами, например http://weblog.jamisbuck.org/2007/2/5/nesting-resources

интересно, что я только что узнал, что большинство модульных тестов людей на самом деле не проверяют все пути. Когда люди следуют предложению джеймисбака, они в конечном итоге получают два способа получить вложенные ресурсы. Их юнит-тесты, как правило, получают / публикуют до самого простого:

# POST /comments
post :create, :comment => {:article_id=>42, ...}

чтобы проверить маршрут, который они может предпочесть, им нужно сделать это таким образом:

# POST /articles/42/comments
post :create, :article_id => 42, :comment => {...}

я узнал это, потому что мои модульные тесты начали терпеть неудачу, когда я переключился с этого:

resources :comments
resources :articles do
  resources :comments
end

для этого:

resources :comments, :only => [:destroy, :show, :edit, :update]
resources :articles do
  resources :comments, :only => [:create, :index, :new]
end

Я думаю, это нормально иметь дубликаты маршрутов, и пропустить несколько модульных тестов. (Зачем тестировать? Потому что даже если пользователь никогда не видит дубликатов, ваши формы могут ссылаться на них, либо неявно, либо через именованные маршруты.) Тем не менее, чтобы свести к минимуму ненужное дублирование, я рекомендую это:

resources :comments
resources :articles do
  resources :comments, :only => [:create, :index, :new]
end

извините за долгий ответ. Не многие люди знают о тонкостях, я думаю.

убедитесь, что оба объекта созданы в контроллер: @post и @comment для поста, например:

@post = Post.find params[:post_id]
@comment = Comment.new(:post=>@post)

затем в поле зрения:

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

обязательно явно определите массив в form_for, а не только через запятую, как у вас выше.

вам не нужно делать специальные вещи в форме. Вы просто строите комментарий правильно в действии show:

class ArticlesController < ActionController::Base
  ....
  def show
    @article = Article.find(params[:id])
    @new_comment = @article.comments.build
  end
  ....
end

а затем сделать форму для него в представлении статьи:

<% form_for @new_comment do |f| %>
   <%= f.text_area :text %>
   <%= f.submit "Post Comment" %>
<% end %>

по умолчанию этот комментарий будет отправлен в create действие CommentsController, который вы, вероятно, захотите поставить redirect :back таким образом, вы направляетесь обратно в Article страница.