防止对特定操作进行ActiveRecord关联验证的最佳方法

时间:2019-12-01 03:37:05

标签: ruby-on-rails validation activerecord associations

我有2个模型:UserPurchase。购买属于用户。

class User < ApplicationRecord
  has_many :purchases
end

class Purchase < ApplicationRecord
  belongs_to :user
  enum status: %i[succeeded pending failed refunded]
end

在Rails 5.2中,如果对Purchase进行任何修改而没有关联的User,则会引发验证错误。这对于新购买的产品非常有用,但是当仅尝试使用数据库中不再存在的用户来保存现有购买的数据时,也会引发错误。

例如:

user = User.find(1)

# Fails because no user is passed
purchase = Purchase.create(status: 'succeeded') 

# Succeeds
purchase = Purchase.create(user: user, status: 'succeeded') 
purchase.status = 'failed'
purchase.save

user.destroy

# Fails because user doesn't exist
purchase.status = 'refunded'
purchase.save

我知道我可以通过在购买模型中将关联与belongs_to :user, optional: true设置为可选来防止第二次更新失败,但是这也会取消对购买创建的第一次验证。

我还可以为用户关联建立自己的自定义验证,但是我正在寻找一种更常规的Rails方法来实现这一点。

2 个答案:

答案 0 :(得分:2)

您可以使用验证上下文https://guides.rubyonrails.org/active_record_validations.html#on

您可以将关系设置为可选,然后仅在创建时添加验证,而不能在更新时添加验证(默认行为为保存):

belongs_to :user, optional: true

validates :user, presence: true, on: :create

答案 1 :(得分:0)

您可以使用if:unless:选项来进行有条件的验证:

class Purchase < ApplicationRecord
  belongs_to :user, optional: true # suppress generation of the default validation
  validates_presence_of :user, unless: :refunded?
  enum status: %i[succeeded pending failed refunded]
end

您可以传递方法或lambda的名称。请勿将它们与ifunless关键字混淆,它们只是关键字参数。

optional:的{​​{1}}选项实际上只是添加了belongs_to验证,没有选项。简而言之,这是很好的选择,但是却没有那么灵活。

相关问题