form_for із вкладеними ресурсами


125

У мене є двоскладове запитання про 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 %>

Це дає помилку: "Викликається ідентифікатор для нуля, який би помилково і т.д." Я також пробував

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

Він відображає правильно, але стосується f.text_area до поля "текст" статті замість коментаря та представляє html для атрибута Article.text у цій текстовій області. Тому я, мабуть, маю і це неправильно. Мені хочеться - це форма, "надіслати", буде викликати дію створення на CommentsController, зі статтею_id у парамах, наприклад, запитом на публікацію до / articles / 1 / comments.

Друга частина мого питання полягає в тому, з чого найкращий спосіб створити екземпляр коментарів для початку? Я створюю @comment у показовій дії ArticleController, тому об’єкт коментаря буде доступний для помічника form_for. Потім у дії на створення CommentsController я створюю новий @comment, використовуючи парами, передані з form_for.

Дякую!

Відповіді:


228

Тревіс R правильний. (Я б хотів, щоб я міг підтримати вас.) Я просто працював над цим сам. За допомогою цих маршрутів:

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

Вибачте за довгу відповідь. Думаю, що не багато людей знають про тонкощі.


Це Work, але мені довелося змінити контролер, як сказав jamuraa.
Маркус Бекер

Шлях Джема працює, але ви можете закінчити додаткові маршрути, про які, напевно, не знаєте. Краще бути явним.
cdunn2001

Я вклав ресурси @result всередині @course. Хоч, [@result, @course]працював, але form_for(@result, url: { action: "create" }) теж працює. Для цього потрібні лише прізвище моделі та ім'я методу.
Анвар

@ cdunn2001 Будь ласка, чи можете ви пояснити, чому ми маємо тут згадати "@article" і що це означає? що робить нижній синтаксис? : <% = form_for [@article, @comment] do | f | %>
Арпіт Агарвал

1
Тревіс / @ cdunn2001 правильно зрозумів. Не встановлюйте і батьківський, і ресурсний, коли ви використовуєте вкладені маршрути без дублікатів, інакше він вважатиме, що всі дії вкладені. Так само якщо ви все вклали, то завжди встановлюйте AT.parent. Крім того, якщо у вас є загальна форма з кнопкою скасування з частково вкладеними маршрутами, тоді використовуйте такий шлях, як описано нижче, так що він працює залежно від того, який ви встановили (Зверніть увагу на плюралізацію дитини): <% = link_to 'Скасувати', parent_children_path (AT.parent || AT.child.parent)%>
iheggie

54

Обов’язково створено обидва об'єкти в контролері: @postі @commentдля повідомлення, наприклад:

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

Тоді перегляд:

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

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


Тревіс - це стара відповідь, але я вважаю, що це найбільш правильно для Rails 3.2.X. Якщо ви хочете, щоб усі елементи конструктора форми заповнили поля Коментар, просто використовуйте масив, URL-помічники не потрібні.
Карл

1
Встановіть лише батьківський об'єкт, де вкладено дію. Якщо ви лише частково вклали ресурс (наприклад, наприклад), то встановлення батьківського об'єкта призведе до виходу з ладу form_for (ще раз підтверджено рейками 5.1)
iheggie

35

Вам не потрібно робити особливі речі у формі. Ви просто побудуєте коментар правильно в шоу-дії:

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сторінку.


10
Довелося використовувати form_for([@article, @new_comment])формат. Я думаю, це тому, що я демонструю думку comments#new, а не article#new_comment. Я вважаю, що в article#new_commentRails досить розумний, щоб розробити, який об’єкт коментаря вкладений, і такого вам не потрібно вказувати?
Суп
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.