wake-up-neo.com

form_for mit verschachtelten Ressourcen

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!

117
Dave Sims

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.

219
cdunn2001

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.

52
Travis Reeder

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.

33
jamuraa