form_for с вложенными ресурсами - PullRequest
116 голосов
/ 09 января 2010

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

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

Форма комментария находится в представлении show.html.erb для статей под самой статьей, например, так:

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

Это выдает ошибку "Called id for nil, что по ошибке и т. Д." Я также попробовал

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

Что правильно отображает, но связывает f.text_area с полем text статьи вместо комментария и представляет HTML-код для атрибута article.text в этой текстовой области. Так что, похоже, я тоже ошибаюсь. То, что мне нужно, это форма, чья 'submit' будет вызывать действие create в CommentsController, с article_id в params, например, запросом на публикацию в / article / 1 / comments.

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

Спасибо!

Ответы [ 3 ]

216 голосов
/ 06 января 2011

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

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

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

52 голосов
/ 27 декабря 2010

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

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

Тогда в поле зрения:

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

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

33 голосов
/ 10 января 2010

Вам не нужно делать специальные вещи в форме. Вы просто правильно строите комментарий в действии 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.

...