【问题标题】:I can't create model objects using accepts_nested_attributes_for. It won't create the nested object我无法使用 Accepts_nested_attributes_for 创建模型对象。它不会创建嵌套对象
【发布时间】:2014-10-02 09:09:22
【问题描述】:

我的模型结构如下所示:

Board has_many 主题。主题 has_many Posts。

app/models/board.rb

class Board < ActiveRecord::Base
    has_many :topics
end

app/models/topic.rb

class Topic < ActiveRecord::Base
    belongs_to :user
    belongs_to :board
    has_many :posts

    accepts_nested_attributes_for :posts

    validates :title, presence: true, length: { maximum: 255 }
    validates :user_id, presence: true
    validates :board_id, presence: true
    ...
end

app/models/post.rb

class Post < ActiveRecord::Base
    belongs_to :user
    belongs_to :topic

    validates :user_id, presence: true
    validates :topic_id, presence: true
    validates :content, length: { minimum: 8 }
end

这是我对创建新主题的看法。 fields_for 部分用于在新帖子上创建 :content

app/views/topics/new.html.erb

<div>
    <%= form_for [@board, @topic] do |f| %>

    <%= render 'shared/error_messages', object: @topic %>

    <%= f.label :title %>
    <%= f.text_field :title %>

    <%= f.fields_for @post do |p| %>
        <%= p.label :content %>
        <%= p.text_area :content %>
    <% end %>

    <%= f.submit "Post new topic", class: "button submit" %>
    <% end %>
</div>

创建新主题时,我希望还创建一个包含表单中 :content 的新帖子。由于帖子依赖于主题才能有效,因此需要同时创建或拒绝它们(如果 :content 或 :title 无效)。有人告诉我,accepts_nested_attributes_for 可以正常工作,但是当我的代码执行时,它只会创建主题,而不是帖子。

app/controllers/topics_controller.rb

def new
    @board = Board.find(params[:board_id])
    @topic = @board.topics.build
    @post = @topic.posts.build
end

def create
    @board = Board.find(params[:board_id])
    @topic = @board.topics.build(topic_params.merge({user_id: current_user.id}))

    if @topic.save
        flash[:success] = "Topic created"
        redirect_to @topic
    else
        render 'new'
    end
end

private

def topic_params
    params.require(:topic).permit(:title, posts_attributes: [:content])
end

作为记录,这是我的 Posts 控制器和路由,如果有帮助的话。

app/controllers/posts_controller.rb

def create
    @topic = Topic.find(params[:topic_id])
    @post = @topic.posts.build(post_params.merge({user_id: current_user.id}))

    if @post.save
        flash[:success] = "Post Created"
        redirect_to topic_path(@topic) 
    else
        render 'new'
    end
  end

  private

    def post_params
      params.require(:post).permit(:content)
    end

为看板、主题和帖子搜索路线

    topic_posts GET    /topics/:topic_id/posts(.:format)      posts#index
                POST   /topics/:topic_id/posts(.:format)      posts#create
 new_topic_post GET    /topics/:topic_id/posts/new(.:format)  posts#new
      edit_post GET    /posts/:id/edit(.:format)              posts#edit
           post GET    /posts/:id(.:format)                   posts#show
                PATCH  /posts/:id(.:format)                   posts#update
                PUT    /posts/:id(.:format)                   posts#update
                DELETE /posts/:id(.:format)                   posts#destroy
   board_topics GET    /boards/:board_id/topics(.:format)     topics#index
                POST   /boards/:board_id/topics(.:format)     topics#create
new_board_topic GET    /boards/:board_id/topics/new(.:format) topics#new
     edit_topic GET    /topics/:id/edit(.:format)             topics#edit
          topic GET    /topics/:id(.:format)                  topics#show
                PATCH  /topics/:id(.:format)                  topics#update
                PUT    /topics/:id(.:format)                  topics#update
                DELETE /topics/:id(.:format)                  topics#destroy
         boards GET    /boards(.:format)                      boards#index
                POST   /boards(.:format)                      boards#create
      new_board GET    /boards/new(.:format)                  boards#new
     edit_board GET    /boards/:id/edit(.:format)             boards#edit
          board GET    /boards/:id(.:format)                  boards#show
                PATCH  /boards/:id(.:format)                  boards#update
                PUT    /boards/:id(.:format)                  boards#update
                DELETE /boards/:id(.:format)                  boards#destroy

还有topics_controller#create开头的params的值

{"utf8"=>"✓", "authenticity_token"=>"...", "topic"=>{"title"=>"新标题", "post"=>{"content"= >"New Content"}},"commit"=>"发布新主题", "action"=>"create", "controller"=>"topics", "board_id"=>"1"}

【问题讨论】:

  • 请注意,您的 params 不包括 post_attributes,正如您的 post_params 所建议的那样。尝试改用f.fields_for @post, as: :post_attributes do |p|
  • 我按照建议修改了 fields_for 但我的参数仍然如下所示:{"utf8"=>"✓", "authenticity_token"=>".....", "topic"=> {"title"=>"NEW TITLE", "post"=>{"content"=>"NEW CONTENT"}}, "commit"=>"发布新主题", "action"=>"create", "控制器"=>"topics", "board_id"=>"2"}
  • 好的。因此,我将其更改为&lt;%= f.fields_for :posts do |p| %&gt;,我的参数现在通过posts_attributes 散列传递。但是现在他们遇到了 Post 模型的验证问题。提交表单时,我收到 2 个表单错误:Posts user can't be blankPosts topic can't be blank
  • 我可以通过在表单中​​包含一个隐藏字段来取消Posts user can't be blank,如下所示:&lt;%= p.hidden_field :user_id, :value =&gt; current_user.id %&gt;。我不认为这是做到这一点的好方法,但我没有更好的想法。我还有Posts topic can't be blank。我无法理解这应该如何工作。 Post 需要一个 topic_id 来保存,但 Topic 在保存之前不会有一个 id。

标签: ruby-on-rails ruby-on-rails-4 associations nested-attributes nested-resources


【解决方案1】:

终于找到解决办法here

这是在我修复表单以正确创建参数之后。

基本上,我需要在我的模型上使用:inverse_of。我真的不明白这是做什么的,但它确实有效。这是我的代码

topic.rb

class Topic < ActiveRecord::Base
  belongs_to :user
  belongs_to :board
  has_many :posts, :inverse_of => :topic

   accepts_nested_attributes_for :posts

  validates :title, presence: true, length: { maximum: 255 }
  validates :user, presence: true
  validates :board, presence: true
end

post.rb

class Post < ActiveRecord::Base
  belongs_to :user
  belongs_to :topic, :inverse_of => :posts

  validates :user, presence: true
  validates :topic, presence: true
  validates :content, presence: true
end

app/views/topics/new.html.erb

<div>
  <%= form_for [@board, @topic] do |f| %>
  <%= render 'shared/error_messages', object: @topic %>

  <%= f.label :title %>
  <%= f.text_field :title %>

  <%= f.fields_for :posts do |p| %>
      <!-- I needed to pass in the current_user.id for the post -->
      <%= p.hidden_field :user_id, :value => current_user.id %>

      <%= p.label :content %>
      <%= p.text_area :content %>
  <% end %>

  <%= f.submit "Post new topic", class: "button submit" %>
  <% end %>
</div>

app/controllers/topics_controller.rb

def create
  @board = Board.find(params[:board_id])
  @topic = @board.topics.build(topic_params.merge({user_id: current_user.id}))
  debugger

  if @topic.save
    flash[:success] = "Topic created"
    redirect_to @topic
  else
    render 'new'
  end
end

private

def topic_params
    params.require(:topic).permit(:title, posts_attributes: [:content, :user_id])
end

【讨论】:

  • 确保参数中传递的user_idcurrent_user 的 id 匹配(现在我可以手动修改表单中的隐藏输入并将其设置为另一个,这将导致创建属于其他人的帖子)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-04-16
  • 2019-04-13
  • 2020-04-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多