代码之家  ›  专栏  ›  技术社区  ›  Cannon Moyer

验证不阻止销毁-Rails 5

  •  0
  • Cannon Moyer  · 技术社区  · 7 年前

    Customer 与多对多关联的记录 Location .

    我当前在“编辑客户”页面上选中/取消选中特定客户的位置。当我取消选中所有位置并点击submit时,这些位置将从我的模型中删除,并从我的验证中抛出一个错误。我希望这些位置不会被删除,因为我有验证,需要它们存在,这是我需要的。

    我的确认 模型。

    validates :locations, presence: true
    

    我的观点:

    <% @locations.each do |i| %>
          <li class="list-group-item">
            <%= hidden_field_tag "customer[location_ids][]", '' %>
            <%= check_box_tag "customer[location_ids][]", i.id, @customer.location_ids.include?(i.id) %>
            <%= i.name %>
          </li>
    <% end %>
    

    我的控制器:

        @customer = Customer.find(params[:id])
        @customer.assign_attributes(params.require(:customer).permit(:first_name, :middle_initial, :last_name, :location_ids => []))
        @customer.save!
    

    .save!

    error Locations can't be blank
    

    但是,当您刷新页面或查看数据库记录时,位置引用将被销毁,我还看到它们在CLI中被销毁。我不明白为什么验证不能阻止位置引用被销毁。提前谢谢。

    3 回复  |  直到 7 年前
        1
  •  1
  •   Wizard of Ogz    7 年前

    这是人们在使用ActiveRecord时遇到的一个常见问题。

    这个 location_ids= ActiveRecord Associations guide . 这种行为常常让开发人员感到意外,而且往往是不受欢迎的。我几乎总是避开它。

    在您的代码中,您首先调用 assign_attributes 反过来又叫 位置\u ID= . 更改会立即保留到记录中。随后,当 save! 调用时,它将打开一个新事务。如果发生验证错误,则只回滚该事务中的更改,而这些更改不包括由所做的已持久化更改 位置\u ID= .

    @customer.assign_attributes(params...)  # location_ids are saved outside of the `save!` transaction.
    @customer.save!                         # validation errors will cause a rollback only to this point, excluding changes from the previous line.
    

    简单的解决方案

    update_attributes! 将两者都替换 分配\u属性 . 这将具有包装事务中所有更改的效果,这样回滚将撤消您想要的所有更改。耶!

    @customer.update_attributes!(params.require(:customer).permit(:first_name, :middle_initial, :last_name, :location_ids => []))
    

    另一种方法

    有时可能无法避免单独呼叫 分配\u属性 save . 这使得事情变得更加复杂。对于这个案子,我想不出任何选择是微不足道的。

    一种可能的解决方案是使用嵌套属性来更新/销毁子记录。

    location_ids 是更新与给定客户关联的每个位置记录的快捷方式。您可以使用表单中的嵌套属性来更新位置,而不是依赖于此。这种方法可以利用 mark_for_destruction link

        2
  •  0
  •   Kedarnag Mukanahallipatna    7 年前

    before_destory 回拨。这将防止在保存客户时删除位置,同时取消选中所有位置。

    class Customer < ActiveRecord::Base
      has_many :locations
      before_destroy :check_for_locations?
    
      private
    
      def check_for_locations?
        errors.add(:base, "cannot delete customer with locations") unless locations.count == 0
        errors.blank?
      end
    end
    
        3
  •  0
  •   ScottM    7 年前

    我发现ActiveRecord通过要求它验证关联记录的长度来验证关联记录的存在更可靠 *_ids 数组。就你而言:

    class Customer < ApplicationRecord
      has_many :locations
      validates :location_ids, length: { minimum: 1 }
    end
    
    推荐文章