form_for con risorse nidificate


125

Ho una domanda in due parti su form_for e risorse nidificate. Diciamo che sto scrivendo un motore di blog e voglio mettere in relazione un commento con un articolo. Ho definito una risorsa nidificata come segue:

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

Il modulo di commento è nella vista show.html.erb per gli articoli, sotto l'articolo stesso, ad esempio in questo modo:

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

Questo dà un errore, "Chiamato id per zero, che erroneamente ecc." Ci ho anche provato

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

Il che viene visualizzato correttamente ma fa riferimento a f.text_area al campo "testo" dell'articolo anziché a quello del commento e presenta l'html per l'attributo article.text in quell'area di testo. Quindi mi sembra di avere anche questo errore. Quello che voglio è un modulo il cui 'submit' chiamerà l'azione di creazione su CommentsController, con un article_id nei parametri, ad esempio una richiesta di post a / articoli / 1 / commenti.

La seconda parte della mia domanda è: qual è il modo migliore per creare l'istanza di commento per cominciare? Sto creando un commento @ nell'azione show di ArticlesController, quindi un oggetto commento sarà nell'ambito dell'helper form_for. Quindi nell'azione di creazione di CommentsController, creo un nuovo @commento usando i parametri passati da form_for.

Grazie!

Risposte:


228

Travis R è corretto. (Vorrei poterti votare.) Ho appena funzionato da solo. Con questi percorsi:

resources :articles do
  resources :comments
end

Ottieni percorsi come:

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

indirizzato ai controller in

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

proprio come dice su http://guides.rubyonrails.org/routing.html#nested-resources , senza spazi dei nomi speciali.

Ma i parziali e le forme diventano difficili. Nota le parentesi quadre:

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

Soprattutto, se vuoi un URI, potresti aver bisogno di qualcosa del genere:

article_comment_path(@article, @comment)

In alternativa:

[@article, @comment]

come descritto su http://edgeguides.rubyonrails.org/routing.html#creating-paths-and-urls-from-objects

Ad esempio, all'interno di una raccolta parziale con comment_itemfornita per l'iterazione,

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

Ciò che dice Jamuraa potrebbe funzionare nel contesto dell'articolo, ma non ha funzionato per me in vari altri modi.

C'è molta discussione relativa alle risorse nidificate, ad esempio http://weblog.jamisbuck.org/2007/2/5/nesting-resources

È interessante notare che ho appena appreso che i test unitari della maggior parte delle persone non stanno effettivamente testando tutti i percorsi. Quando le persone seguono il suggerimento di Jamisbuck, finiscono con due modi per ottenere risorse nidificate. I loro test unitari generalmente ottengono / pubblicano nel modo più semplice:

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

Per testare il percorso che possono preferire, devono farlo in questo modo:

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

Ho imparato questo perché i miei test unitari hanno iniziato a fallire quando sono passato da questo:

resources :comments
resources :articles do
  resources :comments
end

a questa:

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

Immagino sia giusto avere percorsi duplicati e perdere alcuni test unitari. (Perché eseguire il test? Perché anche se l'utente non vede mai i duplicati, i moduli possono fare riferimento a essi, implicitamente o tramite percorsi denominati.) Tuttavia, per ridurre al minimo la duplicazione inutile, consiglio questo:

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

Ci scusiamo per la lunga risposta. Non molte persone sono consapevoli delle sottigliezze, penso.


È lavoro ma, ho dovuto modificare il controller come ha detto jamuraa.
Marcus Becker,

Il modo in cui Jam funziona, ma puoi finire con percorsi extra che probabilmente non conosci. È meglio essere espliciti.
cdunn2001,

Avevo risorse nidificate, @result in @course. [@result, @course]Tuttavia , ha funzionato, ma form_for(@result, url: { action: "create" }) funziona anche. Ciò richiede solo il cognome del modello e il nome del metodo.
Anwar,

@ cdunn2001 Per favore, puoi spiegare perché qui dobbiamo menzionare "@article" e cosa significa? cosa fa la sintassi seguente? : <% = form_for [@article, @comment] do | f | %>
Arpit Agarwal,

1
Travis / @ cdunn2001 ha capito bene. Non impostare sia il genitore che la risorsa quando si utilizzano percorsi nidificati senza duplicati, altrimenti si penserà che tutte le azioni siano nidificate. Allo stesso modo se hai annidato tutto, imposta sempre AT.parent. Inoltre, se si dispone di un modulo comune con un pulsante Annulla con percorsi parzialmente nidificati, utilizzare un percorso come il seguente in modo che funzioni qualunque sia stato impostato (notare la pluralizzazione del figlio): <% = link_to 'Cancel', parent_children_path (AT.parent || AT.child.parent)%>
iheggie,

54

Assicurati di avere entrambi gli oggetti creati nel controller: @poste @commentper il post, ad esempio:

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

Quindi in vista:

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

Assicurati di definire esplicitamente l'array in form_for, non solo una virgola separata come sopra.


Travis è una risposta un po 'vecchia, ma credo che sia la più corretta per Rails 3.2.X. Se desideri che tutti gli elementi del generatore di moduli popolino i campi Commento, utilizza solo un array, non sono richiesti helper di URL.
Karl,

1
Imposta l'oggetto genitore solo dove l'azione è nidificata. Se hai annidato solo parzialmente la risorsa (ad es. Come da esempio), l'impostazione dell'oggetto genitore non riuscirà a form_for (riconfermato con le rotaie 5.1 proprio ora)
iheggie,

35

Non è necessario fare cose speciali nel modulo. Devi solo creare correttamente il commento nell'azione show:

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

e quindi crea un modulo nella vista articolo:

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

per impostazione predefinita, questo commento passerà createall'azione di CommentsController, che probabilmente vorrai inserire redirect :backin modo da essere reindirizzato alla Articlepagina.


10
Ho dovuto usare il form_for([@article, @new_comment])formato. Penso che questo sia perché sto mostrando il punto di vista comments#new, no article#new_comment. Immagino che in article#new_commentRails sia abbastanza intelligente da capire in che cosa è annidato l'oggetto commento e tale non è necessario specificarlo?
Soup
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.