self.errors [:base]<<在ActiveRecord事务块

时间:2015-07-09 08:05:13

标签: ruby-on-rails activerecord

除非用户不符合某些条件,否则我想更新@some_instance。我把标准检查和@ some_instance.save放了!在事务块中,如果其中任何一个失败,则不进行任何事务。这很好用,但我无法返回正确的错误消息。如果用户不符合条件,我想返回原因,或者如果@some_instance没有保存,我想返回该错误。

我的代码:

#some_controller.rb
begin
  Some_Class.transaction do
    return render json: { error: @user.errors }, status: :payment_required, 
      location: new_payment_path unless @user.meets_criteria
    @some_instance.save!
  end
rescue ActiveRecord::RecordInvalid => exception
  render :json => { :error => exception.messages }, status: :unprocessable_entity
rescue => error
  # handle some other exception
end

#User.rb
def meets_criteria
  self.errors[:base] << "Does not meet criteria"
  return false
end

我面临的问题是:当meet_criteria方法返回false时,我希望返回渲染json行来执行。相反,它会在“rescue =&gt; error”中捕获错误。

永远不会执行返回渲染json。

更新

@Gen建议使用before_action而不是在事务块中调用meet_criteria。我认为这是一个更好的实现,但我仍然好奇为什么永远不会调用返回呈现。是因为ActiveRecord会引发错误吗?如果是这样,不应该在RecordInvalid异常中捕获?

3 个答案:

答案 0 :(得分:5)

@TheJKFever,我不确定你的代码是否应该跳转到任何救援区(好吧,我确实通过运行它来确认)。

你的some_validation方法返回false,因此&#34;除非@ user.some_validation&#34;计算结果为true,并使用以下日志输出执行渲染:

Completed 402 Payment Required in 128ms

{"error":{"base":["Some error message"]}}

有关RecordInvalid的详细信息,请参阅ActiveRecord::RecordInvalid API。即,&#34;由保存提升!并创造!当记录无效时#34;。

所以,你的&#34;救援ActiveRecord :: RecordInvalid =&gt;例外&#34;应该处理&#34; @ some_instance.save中的异常!&#34;声明,而不是您的自定义验证。

在您的验证中,您实际上没有引发ActiveRecord :: RecordInvalid异常的代码,并且可能会因另一个错误而失败,这很容易通过详细输出来检查。

为了使用some_validation与&#34; self.errors [:base]&lt;&lt;&#34;首先,您需要将以下语句添加到您的用户模型中:

验证:some_validation

在这种情况下,如果你打电话给&#34; @ user.save!&#34;而不是&#34; @ some_instance.save!&#34;,你将陷入那个&#34;救援ActiveRecord :: RecordInvalid =&gt;例外&#34;块。

PS:@TheJKFever,我在下面看到你的一条评论并想澄清一些事情。验证具有明确定义的目的,即在保存之前验证模型,然后您需要的不是模型验证。您实际需要的是控制器上的before_action,它将检查您的用户是否已准备好用于此类操作(将其视为控制器验证)。是的,您可能需要在您的用户模型上使用某种方法来进行检查。

更新(问题更新后)

@TheJKFever,正如我前面提到的,当我实现你的代码时,我能够执行&#34;返回渲染json:{error:@ user.errors} ...&#34;。因此,如果它失败了,它必须是在meet_criteria调用期间的一些异常,但它不是RecordInvalid异常。由于您将meet_criteria包装到事务中,这意味着它可能会执行一些您希望在@ some_instance.save时回滚的数据库更改!没有成功。最好用meet_criteria包装相同的救援块并找出答案。你创造了!还是保存! meet_criteria中有什么东西?如果是这样,那么它也可以抛出RecordInvalid异常。问题是你的代码RecordInvalid可以被meet_criteria和@ some_instance.save!抛出,并且没有办法在代码中看到哪一个。在任何情况下,如果您使用rescue包装您的meet_criteria,您将能够从中发送您的渲染错误请求。如果您决定使用before_action过滤器,那么您将不得不将整个事务移入其中(假设它需要数据的完整性)。

重点是只有在保存的情况下才会抛出ActiveRecord :: RecordInvalid异常!还是创造!数据无效导致失败。在您的代码中可能不是这种情况,并且抛出了一些其他异常,并且您最终进入&#34; rescue =&gt;错误&#34;块。

答案 1 :(得分:4)

您正在向@user模型添加错误并处理@some_instance上引发的异常。试试@user.errors.messages[:base]

答案 2 :(得分:3)

自定义验证通常不会通过公共实例方法调用。它们通常作为私有方法编写,并在valid?期间由ActiveRecord调用,如果您在模型中注册它们,例如validate :some_validation

在您的情况下,用户模型需要以下内容:

validate :some_validation
…
private
def some_validation
  errors.add :base, "Some error message" if some_error_happens?
end

然后,在控制器中,你会这样做:

Some_Class.transaction do
  return render json: { error: @user.errors }, status: :payment_required, 
    location: new_payment_path if @user.valid?
  @some_instance.save!
end