form_for với tài nguyên lồng nhau


125

Tôi có một câu hỏi gồm hai phần về tài nguyên form_for và lồng nhau. Giả sử tôi đang viết một công cụ blog và tôi muốn liên kết một bình luận với một bài viết. Tôi đã xác định một tài nguyên lồng nhau như sau:

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

Biểu mẫu nhận xét nằm trong chế độ xem show.html.erb cho các bài viết, bên dưới chính bài viết, ví dụ như thế này:

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

Điều này đưa ra một lỗi, "Đã gọi id cho nil, sẽ nhầm, v.v." Tôi cũng đã thử

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

Điều này biểu hiện chính xác nhưng liên quan đến f.text_area với trường 'văn bản' của bài viết thay vì nhận xét và trình bày html cho thuộc tính article.text trong vùng văn bản đó. Vì vậy, tôi dường như cũng có sai này. Những gì tôi muốn là một hình thức mà 'gửi' sẽ gọi hành động tạo trên CommentsContoder, với một article_id trong các thông số, ví dụ như một yêu cầu bài đăng tới / article / 1 / bình luận.

Phần thứ hai cho câu hỏi của tôi là, cách tốt nhất để tạo ra ví dụ bình luận để bắt đầu là gì? Tôi đang tạo một @comment trong hành động hiển thị của ArticleContoder, vì vậy một đối tượng bình luận sẽ nằm trong phạm vi cho trình trợ giúp form_for. Sau đó, trong hành động tạo của CommentsContoder, tôi tạo @comment mới bằng cách sử dụng các tham số được truyền từ form_for.

Cảm ơn!

Câu trả lời:


228

Travis R là chính xác. (Tôi ước tôi có thể nâng cao ya.) Tôi vừa mới làm việc này. Với các tuyến đường này:

resources :articles do
  resources :comments
end

Bạn nhận được các đường dẫn như:

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

chuyển đến bộ điều khiển tại

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

giống như nó nói tại http://guides.rubyonrails.org/routing.html#nested-resours , không có không gian tên đặc biệt.

Nhưng partials và hình thức trở nên khó khăn. Lưu ý dấu ngoặc vuông:

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

Quan trọng nhất, nếu bạn muốn có một URI, bạn có thể cần một cái gì đó như thế này:

article_comment_path(@article, @comment)

Cách khác:

[@article, @comment]

như được mô tả tại http://edgeguides.rubyonrails.org/routing.html#creating-paths-and-urls-from-objects

Ví dụ, bên trong một bộ sưu tập comment_itemđược cung cấp cho phép lặp,

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

Jamuraa nói những gì có thể hoạt động trong bối cảnh của Điều, nhưng nó không hoạt động đối với tôi theo nhiều cách khác nhau.

Có rất nhiều cuộc thảo luận liên quan đến các tài nguyên lồng nhau, ví dụ: http://weblog.jamisbuck.org/2007/2/5/nesting-resours

Thật thú vị, tôi mới biết rằng hầu hết các bài kiểm tra đơn vị của mọi người không thực sự kiểm tra tất cả các đường dẫn. Khi mọi người làm theo đề xuất của jamisbuck, họ kết thúc bằng hai cách để có được các tài nguyên lồng nhau. Các bài kiểm tra đơn vị của họ thường sẽ nhận / đăng bài đơn giản nhất:

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

Để kiểm tra tuyến đường mà họ có thể thích, họ cần thực hiện theo cách này:

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

Tôi đã học được điều này bởi vì các bài kiểm tra đơn vị của tôi bắt đầu thất bại khi tôi chuyển từ điều này:

resources :comments
resources :articles do
  resources :comments
end

đến đây:

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

Tôi đoán sẽ ổn khi có các tuyến trùng lặp và bỏ lỡ một vài bài kiểm tra đơn vị. (Tại sao phải kiểm tra? Bởi vì ngay cả khi người dùng không bao giờ nhìn thấy các bản sao, các biểu mẫu của bạn có thể đề cập đến chúng, mặc nhiên hoặc thông qua các tuyến được đặt tên.) Tuy nhiên, để giảm thiểu sự trùng lặp không cần thiết, tôi khuyên bạn nên điều này:

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

Xin lỗi vì câu trả lời dài. Không có nhiều người nhận thức được sự tinh tế, tôi nghĩ vậy.


Đó là Công việc nhưng, tôi đã phải sửa đổi bộ điều khiển như jamuraa nói.
Marcus Becker

Cách của Jam hoạt động, nhưng bạn có thể kết thúc với các tuyến đường bổ sung mà bạn có thể không biết. Tốt hơn hết là nên nói rõ.
cdunn2001

Tôi đã lồng các tài nguyên, @result bên trong @c thuyết. Mặc dù, [@result, @course]làm việc, nhưng form_for(@result, url: { action: "create" }) cũng làm việc. Điều này chỉ cần tên mô hình cuối cùng và tên phương thức.
Anwar

@ cdunn2001 Xin vui lòng giải thích lý do tại sao chúng ta phải đề cập đến "@article" ở đây như thế này và điều này có nghĩa là gì? cú pháp dưới đây làm gì? : <% = form_for [@article, @comment] do | f | %>
Arpit Agarwal

1
Travis / @ cdunn2001 đã hiểu đúng. Đừng đặt cả cha mẹ và tài nguyên khi bạn đang sử dụng các tuyến được lồng mà không trùng lặp, nếu không, nó sẽ nghĩ rằng tất cả các hành động được lồng nhau. Tương tự như vậy nếu bạn lồng mọi thứ, sau đó luôn đặt AT.parent. Ngoài ra, nếu bạn có một hình thức phổ biến với nút hủy với các tuyến được lồng một phần thì hãy sử dụng một đường dẫn như sau để nó hoạt động bất cứ khi nào bạn đã đặt (Lưu ý số nhiều của con): <% = link_to 'Hủy', Parent_children_path (AT.parent | | AT.child.parent)%>
iheggie

54

Đảm bảo có cả hai đối tượng được tạo trong bộ điều khiển: @post@commentcho bài đăng, ví dụ:

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

Sau đó, trong chế độ xem:

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

Hãy chắc chắn xác định rõ ràng mảng trong form_for, không chỉ phân tách bằng dấu phẩy như bạn có ở trên.


Travis là một câu trả lời cũ, nhưng tôi tin rằng nó là chính xác nhất cho Rails 3.2.X. Nếu bạn muốn tất cả các thành phần của trình tạo biểu mẫu điền vào các trường Nhận xét, chỉ cần sử dụng một mảng, không cần phải có trình trợ giúp url.
Karl

1
Chỉ đặt đối tượng cha mẹ trong đó hành động được lồng. Nếu bạn chỉ lồng một phần tài nguyên (ví dụ như theo ví dụ), thì việc đặt đối tượng cha mẹ sẽ khiến form_for bị lỗi (được xác nhận lại với đường ray 5.1 ngay bây giờ)
iheggie

35

Bạn không cần phải làm những điều đặc biệt trong mẫu. Bạn chỉ cần xây dựng bình luận chính xác trong hành động hiển thị:

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

và sau đó tạo một biểu mẫu cho nó trong chế độ xem bài viết:

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

theo mặc định, nhận xét này sẽ chuyển sang createhành động CommentsController, mà sau đó bạn có thể muốn đưa redirect :backvào để bạn được chuyển trở lại Articletrang.


10
Tôi đã phải sử dụng form_for([@article, @new_comment])định dạng. Tôi nghĩ rằng điều này là bởi vì tôi đang hiển thị quan điểm cho comments#new, không article#new_comment. Tôi nghĩ trong article#new_commentRails đủ thông minh để tìm ra đối tượng bình luận nào được lồng vào và bạn không cần phải chỉ định nó?
Súp
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.