使用accepts_nested_attributes_for

时间:2015-10-26 12:12:21

标签: ruby-on-rails activerecord

出于某种原因。

我想删除并创建而不是嵌套表单的UPDATE。

正常更新正常工作

  def update
    @post = Post.find(params[:id])

    ActiveRecord::Base.transaction do
      @post.update!(update_params)
    end
  rescue ActiveRecord::RecordInvalid
    puts "ERROR"
  end

我想改为喜欢(不工作)

  def update
    @post = Post.find(params[:id])

    ActiveRecord::Base.transaction do
      @post.attributes = update_params

      @post.post_categories.each do |pc|
        if pc.changed?
          pc.mark_for_destruction
        end
      end
      @post.save!
      @post.attributes = update_params
      @post.save!
    end
  rescue ActiveRecord::RecordInvalid
    puts "ERROR"
  end


源代码

这是所有代码的正常更新版本 https://github.com/shingo-nakanishi/workout_rails/tree/v5.0

发表

class Post < ActiveRecord::Base
  has_many :post_categories, inverse_of: :post
  accepts_nested_attributes_for :post_categories,
                                allow_destroy: true,
                                reject_if: :is_reject

  private

  def is_reject(attributes)
    is_exist = attributes[:id].present?
    is_empty = attributes[:post_category_id].blank?
    attributes.merge!(_destroy: 1) if is_exist and is_empty
    !is_exist and is_empty
  end
end

PostCategory

class PostCategory < ActiveRecord::Base
  belongs_to :post, inverse_of: :post_categories
  belongs_to :post_category_master
end

PostCategoryMaster

class PostCategoryMaster < ActiveRecord::Base
end

PostsController

class PostsController < ApplicationController
  def new
    @post = Post.new
    @post.post_categories.build
  end

  def create
    @post = Post.new(create_params)
    @post.save!
  rescue ActiveRecord::RecordInvalid
    puts "ERROR"
  end

  def edit
    @post = Post.find(params[:id])
    unless @post.post_categories.present?
      @post.post_categories.build
    end
  end

  def update
    @post = Post.find(params[:id])

    ActiveRecord::Base.transaction do
      @post.attributes = update_params

      @post.post_categories.each do |pc|
        if pc.changed?
          pc.mark_for_destruction
        end
      end
      @post.save!
      @post.attributes = update_params
      @post.save!
    end
  rescue ActiveRecord::RecordInvalid
    puts "ERROR"
  end

  private
  def create_params
    params.require(:post).permit(:description, post_categories_attributes: [:post_category_id])
  end

  def update_params
    params.require(:post).permit(:description, post_categories_attributes: [:id, :post_category_id])
  end
end

查看/帖/ edit.html.erb

<%= form_for @post do |f| %>
    <%= f.label :description %><%= f.text_field :description %><br/>
    <%= f.fields_for :post_categories do |b| %>
        <%= b.label :post_category_id %>
        <%= b.collection_select :post_category_id,
                                PostCategoryMaster.all,
                                :id,
                                :name,
                                multiple: true,
                                include_blank: 'none',
                                prompt: 'please select' %>
    <% end -%>
    <%= f.submit %>
<% end %>

我试过

请参阅上文 PostsController #update 使用 mark_for_destruction ,然后分配相同的update_params。

获取错误

  

对于ID为36的帖子,找不到ID = 31的PostCategory

如何删除和创建嵌套表单的UPDATE而不是UPDATE。

1 个答案:

答案 0 :(得分:0)

嵌套属性在同一请求中创建嵌套记录。

我认为你基本上对嵌套属性如何工作感到困惑。

嵌套属性允许您在以下时创建:post_categories:

POST "/posts"

并使用以下内容更新帖子和类别:

UPDATE "/posts/:id"

这意味着在现有PostCategories上添加/删除Post将始终使用update(PUT / PATCH)。

但是当你使用collection_select时,它根本不使用嵌套属性。相反,它会创建一个传递给模型*_ids= setter的id数组。在模型上设置post.post_category_ids= [1]时,它会删除/添加关联。

请注意,post_category_ids为复数形式。但是您的表单不正确,因为它不应该在f.fields_for :post_categories块内。

<%= form_for @post do |f| %>
    <%= f.label :description %><%= f.text_field :description %><br/>
    <%= b.label :post_category_ids %>
    <%= b.collection_select :post_category_ids,
                            PostCategoryMaster.all,
                            :id,
                            :name,
                            multiple: true,
                            include_blank: 'none',
                            prompt: 'please select' 
    %>
    <%= f.submit %>
<% end %>
private
  def create_params
    params.require(:post).permit(:description, post_category_ids: [])
  end

  def update_params
    params.require(:post).permit(:description, post_category_ids: [])
  end

创建使用嵌套属性进行添加/删除的表单元素要复杂得多。

嵌套记录的POST / DELETE。

如果您想使用POST / DELETE谓词对相关类别进行操作,则必须创建其他路径和控制器。

请注意,accept_nested_attributes在这种情况下毫无意义,因为它甚至不会被使用。

resources :posts do
  resources :categories
end

然后你会用

POST "/posts/:post_id/categories" # to create a post.
DELETE "/posts/:post_id/categories/:id" # to destroy a post.

您必须为每个类别创建一个表单(或使用button_for)。

<%= @categories.each do |c| %>
  <%= button_to "Delete", post_categories_path(post_id: c.post, id: c), method: :delete, confirm: "Are you sure?" %>
<% end %>