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 %>

สิ่งนี้ทำให้เกิดข้อผิดพลาด "เรียกว่า id สำหรับศูนย์ซึ่งจะผิดพลาดเป็นต้น" ฉันยังพยายาม

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

ซึ่งแสดงผลอย่างถูกต้อง แต่เกี่ยวข้องกับ f.text_area กับฟิลด์ "text" ของบทความแทนที่จะเป็นความคิดเห็นและแสดง html สำหรับแอตทริบิวต์ article.text ในพื้นที่ข้อความนั้น ดูเหมือนว่าฉันจะมีความผิดนี้เช่นกัน สิ่งที่ฉันต้องการคือแบบฟอร์มที่ 'ส่ง' จะเรียกการดำเนินการสร้างบน CommentsController โดยมี article_id ในพารามิเตอร์ตัวอย่างเช่นคำขอโพสต์ไปยัง / บทความ / 1 / ความคิดเห็น

ส่วนที่สองสำหรับคำถามของฉันคือวิธีที่ดีที่สุดในการสร้างอินสแตนซ์ความคิดเห็นเริ่มต้นด้วยวิธีใด ฉันกำลังสร้าง @comment ในการดำเนินการแสดงของ ArticlesController ดังนั้นออบเจ็กต์ความคิดเห็นจะอยู่ในขอบเขตสำหรับ form_for helper จากนั้นในการดำเนินการสร้างของ CommentsController ฉันสร้าง @comment ใหม่โดยใช้พารามิเตอร์ที่ส่งผ่านจาก form_for

ขอบคุณ!

คำตอบ:


228

Travis 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?" %>

สิ่งที่ Jamuraa พูดอาจใช้ได้ผลในบริบทของบทความ แต่ไม่ได้ผลสำหรับฉันในรูปแบบอื่น ๆ

มีการสนทนามากมายที่เกี่ยวข้องกับแหล่งข้อมูลที่ซ้อนกันเช่นhttp://weblog.jamisbuck.org/2007/2/5/nesting-resources

ที่น่าสนใจคือฉันเพิ่งได้เรียนรู้ว่าการทดสอบหน่วยของคนส่วนใหญ่ไม่ได้ทดสอบทุกเส้นทางจริงๆ เมื่อผู้คนทำตามคำแนะนำของ jamisbuck พวกเขาจะได้รับสองวิธีในการเข้าถึงแหล่งข้อมูลที่ซ้อนกัน โดยทั่วไปการทดสอบหน่วยของพวกเขาจะได้รับ / โพสต์เป็นวิธีที่ง่ายที่สุด:

# 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

ขออภัยที่ตอบยาว ฉันคิดว่ามีคนจำนวนไม่น้อยที่ตระหนักถึงรายละเอียดปลีกย่อย


มันใช้งานได้ แต่ฉันต้องปรับเปลี่ยนคอนโทรลเลอร์อย่างที่ jamuraa กล่าว
Marcus Becker

วิธีของ Jam ใช้งานได้ แต่คุณสามารถจบลงด้วยเส้นทางพิเศษที่คุณอาจไม่รู้ จะดีกว่าที่จะชัดเจน
cdunn2001

ฉันมีทรัพยากรที่ซ้อนอยู่ @result ภายใน @course แม้ว่า[@result, @course]ทำงาน แต่form_for(@result, url: { action: "create" }) ก็ใช้ได้ผล ต้องใช้นามสกุลรุ่นและชื่อวิธีการเท่านั้น
อันวาร์

@ cdunn2001 ช่วยอธิบายได้ไหมว่าทำไมต้องพูดถึง "@article" แบบนี้และหมายความว่าอย่างไร ไวยากรณ์ด้านล่างทำอะไร : <% = form_for [@article, @comment] do | f | %>
Arpit Agarwal

1
Travis / @ cdunn2001 เข้าใจถูกแล้ว อย่าตั้งค่าทั้งพาเรนต์และทรัพยากรเมื่อคุณใช้เส้นทางที่ซ้อนกันโดยไม่ซ้ำกันมิฉะนั้นจะคิดว่าการกระทำทั้งหมดซ้อนกัน ในทำนองเดียวกันหากคุณซ้อนทุกอย่างให้ตั้งค่า AT.parent เสมอ นอกจากนี้หากคุณมีรูปแบบทั่วไปที่มีปุ่มยกเลิกที่มีเส้นทางที่ซ้อนกันบางส่วนให้ใช้เส้นทางดังต่อไปนี้เพื่อให้ใช้งานได้ตามที่คุณตั้งไว้ (สังเกตการใช้พหูพจน์ของลูก): <% = link_to 'Cancel', 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 ไม่ใช่แค่คั่นด้วยเครื่องหมายจุลภาคเหมือนที่คุณมีด้านบน


Travis เป็นคำตอบเก่า ๆ แต่ฉันเชื่อว่ามันถูกต้องที่สุดสำหรับ Rails 3.2.X. หากคุณต้องการให้องค์ประกอบทั้งหมดของตัวสร้างฟอร์มเติมข้อมูลในช่องข้อคิดเห็นเพียงใช้อาร์เรย์ไม่จำเป็นต้องใช้ตัวช่วย URL
Karl

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 ฉลาดพอที่จะค้นหาว่าวัตถุความคิดเห็นซ้อนอยู่ในอะไรและคุณไม่จำเป็นต้องระบุหรือไม่
ซุป
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.