ActiveRecord更新模型,同时回滚另一个



让我们来谈谈这个问题的上下文。给出了RubyonRails中的一个电子商务应用程序。让我们以2个模型为例。用户和信用卡。

我的用户在系统中注册后没有问题。信用卡是一个有信用卡信息的模型(是的,我知道PCI合规性,但这不是重点)

在信用卡模型中,我包含了一个回调after_validation,它将根据您的银行对信用卡进行验证。

让我在这里放一些简单的代码。

型号/用户.rb

class User < ActiveRecord::Base
  enum :status, [:active, :banned]
  has_one :credit_card
end

型号/credit_card.rb

class CreditCard < ActiveRecord::Base
  belongs_to :user
  after_validation :validate_at_bank
  def validate_at_bank
    result = Bank.validate(info) #using active_merchant by exemple
    unless result.success
      errors.add {credit_card: "Bank doesn't validate"}
      user.banned!
    end
  end
end

controllers/credit_cards_controller.rb

class CreditCardsController < ApplicationController
  def create
    @credit_card = CreditCard.new(credit_card_params) # from Strong Parameters
    if @credit_card.save
      render #success
    else
      render #failure
    end
  end
end

是什么导致我出现问题当我做一个新的事情时,看起来Rails在ActiveRecord中打开了一个事务。此时,不会向数据库发送任何内容。

当银行拒绝信用卡时,我想禁止该用户使用。我这样做是通过打电话禁止!现在我意识到这次更新是针对同一笔交易的。我可以看到更新,但一旦保存没有进行,两种型号的一切都会回滚。信用卡没有保存(这很好),用户没有保存(因为我想禁止他,所以这不好)

我试图添加一个事务包装器,但这只添加了一个数据库检查点。我可以为禁令创造一个延迟的工作,但在我看来这太过分了。我可以使用after_rollback回调,但我不确定这是正确的方式。我有点惊讶,我以前从未发现过这种情况,这让我相信我的父母不正确,或者我打这个电话的地方不正确。

经过多次回顾和深入研究,我想出了3种方法来处理这种情况。根据你的需求,其中一个应该对你有好处。

  1. 独立的线程和新的数据库连接
  2. 显式保存之前调用validate函数
  3. 将要执行的任务发送到延迟作业

    • 分离线程

    以下答案显示了如何执行此操作。

https://stackoverflow.com/a/20743433/552443

这会起作用,但也不是那么好和简单。

  • 呼叫有效吗?保存前

这是一个非常快速的解决方案。问题是,一个新的开发人员可能会删除有效的?认为.save可以正常工作的行。

  • 呼叫延迟作业

这可以是任何ActionJob提供程序。您可以在单独的线程中将任务发送给被禁止的用户。根据您的设置,这是非常干净的,但不是每个人都需要DelayedJob。

如果您看到任何内容,请将其添加到评论的新解决方案中。

相关内容

  • 没有找到相关文章

最新更新