【问题标题】:Rails 4 Update Nested AttributesRails 4 更新嵌套属性
【发布时间】:2014-05-06 11:00:45
【问题描述】:

更新嵌套属性追加而不是更新有许多关系 我正在尝试使用 Rails 4 Update_attributes

Class Person <ActiveRecord::Base
 has_many :pets
 accepts_nested_attributes_for :pets
end

Class Pet < ActiveRecord::Base
 belongs_to :person
end

在我的控制器中,我收到的参数为 {id: 23, house_no:'22A', 宠物: [{name:'jeffy', type:'dog'}, {name:'sharky', type:'fish'}]}

我的更新方法是

def update
  @Person = Person.find(params[:id])
  if @Person.update(person_params)
    @Person.save
    render 'persons/create', status 200
  else
    render 'persons/create', status 400
  end
end

private
def person_params
  person_params = params.permit(:house_no)
  person_params.merge! ({pets_attributes: params[:pets]}) if params[:pets].present?
  person_params
end

现在当我更新它时 如果这个人已经有一只宠物 然后新的宠物被追加而不是被更新 例如,如果一个 id 为 1 的人有 1 只名为“Tiger”的宠物 我用 2 只名为“Shasha”和“Monti”的宠物更新那个人,然后这个人的记录有 3 只宠物(Tiger、Shasha 和 Monti),而不是更新为 2 只(Shasha 和 Monti)

【问题讨论】:

  • 因为是has_many吧?
  • 是的,它正确地更新了正常的字符串属性
  • @KushJain 您是否忘记在Pet 模型中添加belongs_to :person 关联或它的错字
  • 谢谢帕万,这是一个错字。我已经编辑了它

标签: ruby-on-rails ruby activerecord


【解决方案1】:

您必须将:id 属性添加到:pets_attributes person_params 中的数组。

您需要允许 id 属性更新记录。

【讨论】:

  • 这是唯一实际正确的答案。 :id 至关重要。 destroy 的解释也很有意义,但这实际上回答了 OP 的问题。
  • @Aleks 那么这和destroy 的解释有什么区别?这对我有用,但后者是官方记录的。
  • @AkashAggarwal 你怎么知道你想销毁哪条记录?你需要它的id。每个人都提到了_destroy,但 OP 询问如何更新,而不是销毁。如果您也想修复破坏,那么您当然需要添加_destroy,但对于更新,您需要id
  • 这是您需要更新记录时的正确答案
【解决方案2】:

参见手册:

http://api.rubyonrails.org/v4.0.1/classes/ActiveRecord/NestedAttributes/ClassMethods.html

您需要发送您的属性,例如pets_attributes:[{name:'jeffy', type:'dog'}, {name:'sharky', type:'fish'}]

它应该可以正常工作。

请阅读

您现在可以通过成员的属性散列设置或更新关联帖子的属性:将关键字 :posts_attributes 包含在帖子属性的散列数组中作为值。对于每个没有 id 键的散列将实例化新记录,除非哈希还包含评估为 true 的 _destroy 键。

【讨论】:

  • 这仅在您不小心留下了至关重要的:id 参数时才有效。为了避免重复在pets_attributes 中放置一个:id
  • 如果您使用强参数,那么您可以在 permit 之后将 pets 重命名为 pets_attributes,这有点像 OP 的 merge 代码,如下所示:params[:pets_attributes] = params.delete(:pets) if params[:pets] - 为什么ActiveRecord 本身并不能很好地处理接收属性的散列,这超出了我的范围:-/
  • 同意pets_attributes但还需要在pets对象中添加:id
【解决方案3】:

更新Person模型如下:

class Person < ActiveRecord::Base  ## NOTE: class (c lowercase) and NOT Class
 has_many :pets
 accepts_nested_attributes_for :pets, allow_destroy: true  ## Added allow_destroy
end

为了通过属性哈希销毁关联模型,即Pet,您必须首先使用:allow_destroy 选项启用它。

然后,从您的角度来看,您需要为您想要移除的宠物传递_destroy 属性。

从那以后,您还没有共享视图特定的代码。以下是如何在您的视图中实现它的示例。根据您的要求更改它:

<%= form_for @person do |f| %>
  <%## .. %>  
  <%= f.fields_for :pets do |builder| %>
    <%## .. %>    
    <%= builder.check_box :_destroy %>
    <%= builder.label :_destroy, "Remove Pet" %>
  <% end %>
<% end %>

在上面的代码中,您需要添加checkboxlabel 以传递_destroy 值。

在此之后,更新控制器中的person_params 方法,如下所示:

def person_params
  params.require(:person).permit(:house_no, pets_attributes: [:id, :name, :type, :person_id, :_destroy])
end 

注意:

不应该使用大写名称定义instance variables。大写名称用于在Ruby 中声明Constants

def update
  @person = Person.find(params[:id])
  if @person.update(person_params)
    @person.save
    render 'persons/create', status 200
  else
    render 'persons/create', status 400
  end
end  

【讨论】:

    【解决方案4】:

    您需要使用 pets_attributes 编辑参数

    还需要在宠物对象中添加:id参数。

    【讨论】:

      【解决方案5】:

      可能 1. 你想单独分配宠物,

      @Person.pets = Pets.find(params[:pets])
      

      或者 2. 在保存人之前清除宠物。

      @Person.pets.destroy
      

      【讨论】:

        【解决方案6】:

        尝试将您的 person_params 更改为

        def person_params
          person_params = params.require(:person).permit(:house_no ,pets_attributes: [:name,:type])
        end
        

        并从更新方法中删除@person.save。你不需要它。还可以使用! 替换update

        def update
          @Person = Person.find(params[:id])
          if @Person.update!(person_params)
            render 'persons/create', status 200
          else
            render 'persons/create', status 400
          end
        end
        

        更新

        而且,正如我所见,Pet 模型中没有 belongs_to :person 关联,请将其添加到模型中

        Class Pet < ActiveRecord::Base
        
        belongs_to :person
        
        end
        

        现在通过在params 中添加person_id 属性来允许它

        def person_params
          person_params = params.require(:person).permit(:house_no,pets_attributes: [:name,:type,:person_id])
        end
        

        【讨论】:

        • 对于 params.require(:person).permit(:house_no) 的第一次更改,它给出了 ActionController::ParameterMissing: param not found: person 甚至在添加了 bang 并删除了 person.save 之后它仍然将其更新为 3 只宠物而不是 2 只
        • 它还是给了我 3 只宠物
        • @Pavan 我想在人的更新方法中“创建”。我的意思是人可能会更新,但如果我填充宠物,它应该创建新的。
        猜你喜欢
        • 2023-03-18
        • 1970-01-01
        • 2013-11-03
        • 1970-01-01
        • 2013-08-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多