Ich habe eine zweiteilige Frage zu form_for und verschachtelten Ressourcen. Angenommen, ich schreibe eine Blog-Engine und möchte einen Kommentar zu einem Artikel veröffentlichen. Ich habe eine verschachtelte Ressource wie folgt definiert:
map.resources :articles do |articles|
articles.resources :comments
end
Das Kommentarformular befindet sich in der Ansicht show.html.erb für Artikel unter dem Artikel selbst, zum Beispiel wie folgt:
<%= render :partial => "articles/article" %>
<% form_for([ :article, @comment]) do |f| %>
<%= f.text_area :text %>
<%= submit_tag "Submit" %>
<% end %>
Dies gibt den Fehler "Called id for nil, was fälschlicherweise der Fall wäre, etc." Ich habe es auch versucht
<% form_for @article, @comment do |f| %>
Was korrekt gerendert wird, aber f.text_area nicht mit dem Kommentar, sondern mit dem Textfeld des Artikels verknüpft, und das HTML für das Attribut article.text in diesem Textbereich anzeigt. Also ich scheine das auch falsch zu haben. Was ich möchte, ist ein Formular, dessen 'submit' die Aktion create auf CommentsController mit einer article_id in den Parametern aufruft, zum Beispiel eine Post-Anfrage an/articles/1/comments.
Der zweite Teil meiner Frage lautet: Wie erstelle ich die Kommentarinstanz am besten? Ich erstelle ein @kommentar in der show-Aktion des ArticlesControllers, sodass ein Kommentarobjekt für den form_for-Helfer im Gültigkeitsbereich ist. Dann erstelle ich in der Aktion create des CommentsControllers ein neues @kommentar mit den Parametern, die aus form_for übergeben wurden.
Vielen Dank!
Travis R ist richtig. (Ich wünschte, ich könnte dich unterstützen.) Ich habe das gerade selbst zum Laufen gebracht. Mit diesen Routen:
resources :articles do
resources :comments
end
Sie erhalten Pfade wie:
/articles/42
/articles/42/comments/99
an Steuerungen unter weitergeleitet
app/controllers/articles_controller.rb
app/controllers/comments_controller.rb
genau wie unter http://guides.rubyonrails.org/routing.html#nested-resources angegeben, ohne spezielle Namespaces.
Aber Partials und Formen werden knifflig. Beachten Sie die eckigen Klammern:
<%= form_for [@article, @comment] do |f| %>
Am wichtigsten ist, wenn Sie eine URI möchten, benötigen Sie möglicherweise Folgendes:
article_comment_path(@article, @comment)
Alternative:
[@article, @comment]
wie beschrieben unter http://edgeguides.rubyonrails.org/routing.html#creating-paths-and-urls-from-objects
Zum Beispiel in einer Sammlung, die teilweise mit comment_item
zur Iteration geliefert,
<%= link_to "delete", article_comment_path(@article, comment_item),
:method => :delete, :confirm => "Really?" %>
Was Jamuraa sagt, mag im Kontext des Artikels funktionieren, aber es hat auf verschiedene andere Arten für mich nicht funktioniert.
Es wird viel über verschachtelte Ressourcen diskutiert, z. http://weblog.jamisbuck.org/2007/2/5/nesting-resources
Interessanterweise habe ich gerade erfahren, dass die meisten Unit-Tests nicht alle Pfade testen. Wenn Leute jamisbucks Vorschlag folgen, haben sie zwei Möglichkeiten, auf verschachtelte Ressourcen zuzugreifen. Ihre Unit-Tests werden in der Regel am einfachsten erhalten/posten:
# POST /comments
post :create, :comment => {:article_id=>42, ...}
Um die Route zu testen, die sie bevorzugen, müssen sie dies folgendermaßen tun:
# POST /articles/42/comments
post :create, :article_id => 42, :comment => {...}
Ich habe das gelernt, weil meine Unit-Tests fehlgeschlagen sind, als ich von hier aus wechselte:
resources :comments
resources :articles do
resources :comments
end
dazu:
resources :comments, :only => [:destroy, :show, :edit, :update]
resources :articles do
resources :comments, :only => [:create, :index, :new]
end
Ich denke, es ist in Ordnung, doppelte Routen zu haben und ein paar Unit-Tests zu verpassen. (Warum testen? Denn selbst wenn der Benutzer die Duplikate nie sieht, verweisen Ihre Formulare möglicherweise implizit oder über benannte Routen auf sie.) Um unnötige Duplikate zu minimieren, empfehle ich Folgendes:
resources :comments
resources :articles do
resources :comments, :only => [:create, :index, :new]
end
Entschuldigung für die lange Antwort. Nicht viele Leute sind sich der Feinheiten bewusst, denke ich.
Stellen Sie sicher, dass beide Objekte im Controller erstellt wurden: @post
und @comment
für die Post, zB:
@post = Post.find params[:post_id]
@comment = Comment.new(:post=>@post)
Dann im Blick:
<%= form_for([@post, @comment]) do |f| %>
Stellen Sie sicher, dass Sie das Array explizit in form_for definieren, nicht nur durch Kommas getrennt, wie oben beschrieben.
Sie müssen keine besonderen Dinge in der Form tun. Sie erstellen den Kommentar einfach korrekt in der Show-Aktion:
class ArticlesController < ActionController::Base
....
def show
@article = Article.find(params[:id])
@new_comment = @article.comments.build
end
....
end
und dann mach ein Formular dafür in der Artikelansicht:
<% form_for @new_comment do |f| %>
<%= f.text_area :text %>
<%= f.submit "Post Comment" %>
<% end %>
standardmäßig wird dieser Kommentar an die Aktion create
von CommentsController
weitergeleitet, in die Sie dann wahrscheinlich redirect :back
einfügen möchten, damit Sie zurück zu Article
Seite.