web-dev-qa-db-fra.com

form_for avec des ressources imbriquées

J'ai une question en deux parties sur les ressources form_for et imbriquées. Disons que j'écris un moteur de blog et que je veux relier un commentaire à un article. J'ai défini une ressource imbriquée comme suit:

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

Le formulaire de commentaire est dans la vue show.html.erb pour les articles, sous l'article lui-même, par exemple comme ceci:

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

Cela donne une erreur, "Appelé id pour nil, ce qui serait par erreur, etc." J'ai aussi essayé

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

Ce qui rend correctement mais relie f.text_area au champ 'texte' de l'article au lieu du commentaire, et présente le code HTML pour l'attribut article.text dans cette zone de texte. Donc, il semble que je me trompe aussi. Ce que je veux, c'est un formulaire dont l'option 'submit' appellera l'action de création sur CommentsController, avec un article_id dans les paramètres, par exemple une demande de publication dans/articles/1/comments.

La deuxième partie de ma question est la suivante: quel est le meilleur moyen de créer l’instance de commentaire pour commencer? Je crée un @comment dans l'action show de ArticlesController. Un objet de commentaire sera donc dans la portée de l'aide form_for. Ensuite, dans l'action de création de CommentsController, je crée un nouveau commentaire avec les paramètres transmis à partir de form_for.

Merci!

117
Dave Sims

Travis R est correct. (J'aimerais pouvoir vous inviter à passer à autre chose.) Je viens de travailler moi-même. Avec ces itinéraires:

resources :articles do
  resources :comments
end

Vous obtenez des chemins comme:

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

acheminé aux contrôleurs à

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

comme il est dit à http://guides.rubyonrails.org/routing.html#nested-resources , sans espace de nom particulier.

Mais les partiels et les formes deviennent délicats. Notez les crochets:

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

Le plus important, si vous voulez un URI, vous aurez peut-être besoin de quelque chose comme ça:

article_comment_path(@article, @comment)

Alternativement:

[@article, @comment]

comme décrit sur http://edgeguides.rubyonrails.org/routing.html#creating-paths-and-urls-from-objects

Par exemple, dans une collection partielle avec comment_item fourni pour l'itération,

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

Ce que jamuraa dit peut fonctionner dans le contexte d’Article, mais cela ne m’a pas fonctionné de diverses autres manières.

Il y a beaucoup de discussions liées aux ressources imbriquées, par exemple. http://weblog.jamisbuck.org/2007/2/5/nesting-resources

Fait intéressant, j'ai appris que les tests unitaires de la plupart des gens ne testent pas tous les chemins. Lorsque les gens suivent la suggestion de jamisbuck, ils se retrouvent avec deux manières d'obtenir des ressources imbriquées. Leurs tests unitaires seront généralement les suivants:

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

Afin de tester l'itinéraire qu'ils préfèrent, ils doivent le faire de la manière suivante:

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

J'ai appris cela parce que mes tests unitaires ont commencé à échouer lorsque je suis passé de ceci:

resources :comments
resources :articles do
  resources :comments
end

pour ça:

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

Je suppose qu'il est acceptable d'avoir des itinéraires en double et de rater quelques tests unitaires. (Pourquoi tester? Parce que même si l'utilisateur ne voit jamais les doublons, vos formulaires peuvent s'y référer, de manière implicite ou via des itinéraires nommés.) Néanmoins, pour minimiser les duplications inutiles, je vous recommande ceci:

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

Désolé pour la longue réponse. Peu de gens connaissent les subtilités, je pense.

219
cdunn2001

Assurez-vous que les deux objets sont créés dans le contrôleur: @post et @comment pour le poste, par exemple:

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

Alors en vue:

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

Assurez-vous de définir explicitement le tableau dans le formulaire_for, pas seulement séparé par des virgules comme ci-dessus.

52
Travis Reeder

Vous n'avez pas besoin de faire des choses spéciales dans le formulaire. Vous venez de construire le commentaire correctement dans l'action show:

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

puis créez un formulaire dans la vue de l'article:

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

par défaut, ce commentaire ira à l'action create de CommentsController, que vous voudrez probablement mettre redirect :back into afin que vous soyez redirigé vers la page Article.

33
jamuraa