【问题标题】:Refactoring ActiveRecord custom validations重构 ActiveRecord 自定义验证
【发布时间】:2013-09-10 13:24:38
【问题描述】:

我有一个名为PaymentNotifications 的模型。只有在 Paypal 有效时,它才用于记录付款。我需要检查他们给我的交易代码是否与我在发布表格后从他们那里得到的交易代码相同。

所有这些都有效。然后我要做的是根据以下一些标准检查它是否有效:

在控制器中我有以下内容:

tx = params[:tx] 
paypal_data = get_data_from_paypal(tx)
res_hash = create_hash(paypal_data) 
@payment_notification = PaymentNotification.new(:params => res_hash, :quotation_id => res_hash['invoice'],:status => res_hash["payment_status"],:transaction_id => res_hash["txn_id"])
if paypal_data["SUCCESS"] && @payment_notification.is_valid?(tx) && @payment_notification.save
  redirect_to thankyou_path(:id => @payment_notification.quotation_id)
else
  render '/pages/error'
end

然后在模型中我运行我的方法is_valid?

validates :params, :quotation_id, :status, :transaction_id, presence: true
validates :transaction_id, :uniqueness => true

def is_valid?(tx)
  amount_paid_valid?(params["payment_gross"]) && transaction_valid?(tx) && is_quotation_unpaid? 
end

def transaction_valid?(tx)
  if tx != transaction_id
    errors.add(:transaction_id, "This transaction is not valid")
    return false
  else
    return true
  end
end

def is_quotation_unpaid?
  if quotation.unpaid?
    return true
  else
    errors.add(:quotation_paid, "This quotation has already been paid.")
    return false
  end
end

def amount_paid_valid?(amount_paid)
  if amount_paid.to_i == quotation.price.to_i
    return true
  else
    errors.add(:amount_paid, "The amount paid does not match the price quoted.")
    return false
  end
end

注意::amount_paid:quotation_paid 不是属性。它们只是错误消息的键。

我想我在这里错过了这艘船,因为必须有一种方法可以通过 Rails 内置的验证来做到这一点,但我对 Rails 还不是很擅长。有人可以帮我重构它,以便更容易维护并符合最佳实践吗?

【问题讨论】:

    标签: ruby-on-rails validation refactoring ruby-on-rails-4 rails-activerecord


    【解决方案1】:

    这里的主要问题是您正在重新实现 Rails 已有的东西——即检查 AR 对象是否有效的方法。如果您使用您的方法而不是内置的 #valid?,您的对象将继续传递诸如 #save#create 之类的操作,即使它们不应该传递。

    为了在调用内置验证时将您的自定义方法纳入其中并包含它们,只需将它们用作模型中的自定义验证,如下所示:

    validates :params, :quotation_id, :status, :transaction_id, presence: true
    validates :transaction_id, :uniqueness => true
    validate :amount_paid_should_match_quote, :quotation_should_be_unpaid
    validates_associated :transaction
    
    private
    
    def amount_paid_should_match_quote
      if amount.to_i != quotation.price.to_i
        errors.add(:amount, "does not match the price quoted")
      end
    end
    
    def quotation_should_be_unpaid
      if quotation.paid?
        errors.add(:quotation, "has already been paid")
      end
    end
    

    需要注意的几点:

    1. 验证方法不应带参数,因为它们是测试现有属性的实例方法。
    2. 避免在模型中引用参数。处理请求是控制器的工作。
    3. 您只需要在您的方法中处理非通过场景。当对象有效时,不用担心返回true,这取决于 Rails。
    4. 不要编写方法来验证关联。只需使用 validates_associated 即可。
    5. 如果您将自定义方法重命名为更能描述他们试图强制执行的实际行为,这会有所帮助。我试图给你一个建议,但你可以使用任何你喜欢的东西。

    您可以在Rails Guides Validations documentation 了解有关自定义验证的更多信息。

    【讨论】:

    • 非常感谢 depa,这是一个很棒的答案,应该作为其他人匹配的模型。我将落实您的所有建议并从中学习。我应该注意到一件事,与交易无关。那只是用来记录从paypal发送的交易ID。我需要检查从贝宝发送的交易 ID 是否与他们在我发送表格后再次发送的交易 ID 匹配。所以我不是在验证关联,而是在验证 id 是否匹配,所以我必须保留该方法。
    • 嘿,很高兴为您提供帮助。关于关联,我认为transaction_idbelongs_toTransaction 模型的外键。但这没关系。关于验证关联的建议原则上仍然有效。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-02-01
    • 1970-01-01
    • 2021-08-03
    • 1970-01-01
    • 1970-01-01
    • 2014-08-31
    • 1970-01-01
    相关资源
    最近更新 更多