如何挽救控制器和 re-render 表单中的 ActiveRecord::RecordNotUnique 错误?

how to rescue ActiveRecord::RecordNotUnique error in controller and re-render form?

我的控制器的#create 操作失败,因为我已将唯一性约束添加到我的 Boo 模型的名称和标题属性中。

def create
 @boo = current_user.my_boos.create(boo_params)
 respond_to do |format|
  if @boo.save
    format.html { redirect_to root_path, notice: "Thanks!" }
    format.json { render :index, status: :created, location: @boo }
  else
    format.html { render :new }
    format.json { render json: @boo.errors, status: :unprocessable_entity }
  end
 end
end

通过此操作提交会出现看起来像 Railsy 的 PG::UniqueViolation: ERROR: duplicate key value violates unique constraint "index_boos_on_name_and_title" 错误,我认为将其显示为错误消息和 re-rendered 页面会更好。

我正在阅读的所有内容都说从 Exception 中拯救是不好的,而从 StandardError 中拯救是好的。但我还没有找到任何解释如何在不从异常中解救的情况下为最终用户更好地显示该错误的内容。就像,StandardError 似乎不适用于数据库。

直接的答案是拯救你想从中恢复的特定异常,然后按照你认为合适的方式处理它......像这样:

def create
  @boo = current_user.my_boos.new(boo_params)
  respond_to do |format|
    begin
      if @boo.save
        format.html { redirect_to root_path, notice: "Thanks!" }
        format.json { render :index, status: :created, location: @boo }
      else
        format.html { render :new }
        format.json { render json: @boo.errors, status: :unprocessable_entity }
      end
    rescue PG::UniqueViolation
      format.html { render :new }
      format.json { render json: ["We've already got one"], status: :unprocessable_entity }
    end
  end
end

(拯救 StandardError 应该也能正常工作,但虽然安全,但它比我们需要的要广泛得多。)

但是,我建议一个更"Railsy"的解决方案是在你的模型中定义一个唯一性验证,除了数据库约束,所以它'将由现有的 if @boo.save 条件处理。

您可以为您的 Boo 模型添加验证,它将阻止尝试保存无效记录并且无需从 PG::UniqueViolation 错误中拯救:

class Boo < ApplicationRecord
  # ...
  validates :name, uniqueness: { scope: :title }
  # ...
end

(c) http://guides.rubyonrails.org/active_record_validations.html#uniqueness