在destroy_all中捕获错误

时间:2017-08-10 02:04:41

标签: ruby-on-rails ruby ruby-on-rails-5

我有以下行动:

# DELETE /my_controller/ids[]
def destroy_multiple
  ids = params[:ids]

  ActiveRecord::Base.transaction do
    begin          
      SomeModel.where(id: ids).destroy_all
      head :no_content
    end
rescue => e
  p "e #{e.inspect}" # Never called
end

您可能已经注意到,它负责删除许多记录,并且有效...

some_model.rb

before_destroy :check_default_item

private

def check_default_item
  return unless default_item?

  errors.add(:base, 'Cannot delete record because it is a default item')
  p "errors #{errors.inspect}" # I can see the errors
  throw(:abort)
end

假设我有以下记录:

[
  { id: 1, default_item: true },
  { id: 2, default_item: true },
  { id: 3, default_item: true }
]

如果我发送这样的请求:

my_url/destroy_multiple?ids[]=3&ids[]=2&ids[]=1

我可以看到终端中的错误,如下所示:

errors #<ActiveModel::Errors:0x007fdb88364050 @base=#<SomeModel id: 3, default_item: true, created_at: "2017-08-06 18:33:03", updated_at: "2017-08-06 18:33:03">, @messages={:base=>["Cannot delete record because it is a default item"]}, @details={:base=>[{:error=>"Cannot delete record because it is a default item"}]}>

...但它给了我204响应,而不是422。

由于我正在调用throw(:abort),为什么它不会抛出错误/异常?它甚至没有去rescue

请注意,如果我尝试删除这样的单个记录:

@item.destroy!

它有效,我收到&#34;无法删除记录,因为它是默认项目&#34;消息。

1 个答案:

答案 0 :(得分:1)

destroy_all方法只是循环播放并在每条记录上调用destroy,而不是destroy!

def destroy_all
  records.each(&:destroy).tap { reset }
end

如果destroy失败,destroy!只会返回false(来自throw(:abort)

  

如果before_destroy回调抛出:abort则取消操作并且destroy返回false。

这意味着如果您想要引发错误(就像使用https://codepen.io/anon/pen/OjmGMy?editors=0000时那样),您将需要使用自定义方法(没有destroy_all!)。

由于destroy_all的正文相对简单,您可以在代码中复制它:

SomeModel.where(id: ids).each(&:destroy!)

应该这样做,如果你没有尝试重用任何关系(因此不需要它reset)。