[英]rails 5 validate count in associated scope
我有一个名为Person的模型和一个名为Contract的模型。 一个人 的has_many 合同 ,并belongs_to的一个人 合同 。
合同具有开始日期和结束日期 。 如果当前日期介于此日期之间,则合同被视为有效 。
我在合同上有一个名为“ active ”的范围 ,该范围相应地返回记录。 一个人可以有任意数量的无效合同,但应该只有一份有效合同。
我正在尝试找出添加验证的最佳方法,以防止出现以下任何一种情况:
这是我目前正在做的,并且似乎可以正常工作:
class Contract < ApplicationRecord
belongs_to :person
validates_uniqueness_of :person_id, conditions: -> { active }
scope :active, -> { where("start_date <= ? AND end_date >= ?", Date.today, Date.today) }
end
对我来说,这有点像破解。 我不关心唯一性,我关心大小; 碰巧唯一性有效。 如果我要允许不超过2个有效合同怎么办?
另外,当我尝试添加多个活动合同时返回的验证错误是“已经有人”,这是一种误导。 当然,我可以添加自定义消息,但这似乎又表明我做错了。
对于唯一性误导性消息,您始终可以在验证时自定义消息
validates :person_id, uniqueness: {scope: :active, message: 'Some custom message'}
对于非标准验证,您需要使用自定义验证器或自定义验证方法https://guides.rubyonrails.org/active_record_validations.html#custom-methods 。 当某些情况发生时,您只需要为属性添加错误(如果不与属性相关,则为:base),条件可能是检查日期,用户等。
class Contract < ApplicationRecord
validate :allow_only_two_active_contracts
private
def allow_only_two_active_contracts
person_contracts = person.contracts.active.where.not(id: self.id).count #count all active contracts of the person except this one, not sure if the where.not is necessary
errors.add(:person_id, 'This person already has two active contracts') if person_contracts >= 2
end
end
ActiveRecord试图验证您的合同的内容将调用该方法,并且在所有验证之后,如果errors
已使记录无效,则该记录无效。
如果我正确地理解了完整的场景,那么您不仅应该检查唯一性->活动。 否则,当您的有效合同变为非活动状态时-可能有两个无效的合同现在变为活动状态。 我同意验证应该发生在该人身上(这甚至应该解决不断变化的关系中的问题)。
所以我想你想要更多类似的东西:
class Person < ApplicationRecord
validates :contracts, :not_overlapping
end
class NotOverlappingValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
return if value.blank?
mark_overlapping_error to_ranges(value)
end
def mark_overlapping_error(ranges)
ranges[0..-2].each_with_index do |range, index|
# check if successive contracts are overlapping in their active interval
next unless range.overlaps?(ranges[index + 1])
# add some custom error about the overlapping
return record.errors.add attribute, :overlaps
end
end
def to_ranges(contracts)
# mapping contracts to their activity-interval
value.sort_by(&:start_date).map do |contract|
(contract.start_date..contract.end_date)
end
end
end
这样,您可以确保从一开始就确保一个人永远不会获得重叠的合同。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.