【问题标题】:Best way to prevent ActiveRecord association validations on specific actions防止对特定操作进行 ActiveRecord 关联验证的最佳方法
【发布时间】:2019-12-01 03:37:05
【问题描述】:

我有 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 中,当对没有关联 UserPurchase 进行任何修改时,会引发验证错误。这对新购买非常有效,但在尝试保存数据库中不再存在的用户的现有购买数据时也会引发错误。

例如:

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 方法来执行此操作。

【问题讨论】:

    标签: ruby-on-rails validation activerecord associations


    【解决方案1】:

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

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

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

    【讨论】:

      【解决方案2】:

      您可以使用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 关键字混淆 - 它们只是关键字参数。

      belongs_tooptional: 选项实际上只是添加了一个没有选项的 validates_presence_of 验证。这是一个很好的速记,但不是那么灵活。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-07-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-02-23
        相关资源
        最近更新 更多