Model
首先要先有一個root instance
class ForumThread < ApplicationRecord
belongs_to :user
has_many :forum_posts accepts_nested_attributes_for :forum_posts validates :subject, presence: true
validates_associated :forum_posts
end
接著有關聯
class ForumPost < ApplicationRecord
belongs_to :user
belongs_to :forum_thread, optional: true validates :body
end
Controller
def new
@thread = ForumThread.new
@thread.forum_posts.new
enddef create
@thread = current_user.forum_threads.new forum_thread_params
@thread.forum_posts.first.user_id = current_user.id if @thread.save
redirect_to @thread
else
render 'new'
end
end
必須要在controller 中new出 nested attribute的物件,才會在view中被顯示出來。
view
<%= form_for @forum_thread do |f| %>
<%= f.text_field :subject %> <%= f.fields_for :forum_posts do |p| %>
<%= p.text_area :body %>
<% end %>
<%= f.submit %><% end %>
送出去的params 中,除了 form_thread table 的column當key之外,還多了 forum_posts_attributes 來傳nested_attribute的值
江湖一點訣,說破不值錢 — — nested form in Rails 5
在Rails5中,如果有關聯性的資料要被產生而缺少foreign key的話,會產生錯誤,例如
=> #<ActiveModel::Errors:0x007fa862ee5298 @base=#<ForumThread id: nil, user_id: 1, subject: "subject", created_at: nil, updated_at: nil>, @messages={:"forum_posts.forum_thread"=>["must exist"], :forum_posts=>["is invalid"]}, @details={:"forum_posts.forum_thread"=>[{:error=>:blank}], :forum_posts=>[{:error=>:invalid, :value=>#<ActiveRecord::Associations::CollectionProxy [#<ForumPost id: nil, forum_thread_id: nil, user_id: 1, body: "I amm body", created_at: nil, updated_at: nil>]>}]}>
irb(main):014:0> post
以ForumThread跟ForumPost為例,在nested_form中,forum_thread還沒有被create出來,只是被new出來的而已;因此,forum_post還無法抓取到forum_thread的id,但由於forum_post是被forum_thread的instance給new出來的,產生
「forum_thread找得到forum_post,但forum_post找不到forum_thread」
的窘境
因此,必須要在 ForumPost的model中修改條件
class ForumPost < ApplicationRecord
belongs_to :user
belongs_to :forum_thread, optional: true validates :body, presence: true
end
必須加入 optional: true
讓forum_post被產生的時候,不一定要有foreign_key