我的模型有两列(started_at
和ended_at
)。我想添加一个自定义验证器,确保没有其他记录存在日期与我正在验证的记录重叠。到目前为止,我有:
# app/models/event.rb
class Event < ActiveRecord::Base
validates_with EventValidator
end
# app/validators/event_validator.rb
class EventValidator < ActiveModel::Validator
attr_reader :record
def validate(record)
@record = record
validate_dates
end
private
def validate_dates
started_at = record.started_at
ended_at = record.ended_at
arel_table = record.class.arel_table
# This is where I'm not quite sure what type of query I need to perform...
constraints = arel_table[:started_at].gteq(ended_at)
.and(arel_table[:ended_at].lteq(started_at))
if record.persisted?
constraints = constraints
.and(arel_table[:id].not_eq(record.id))
end
if record.class.where(constraints).exists?
record.error[:base] << "Overlaps with another event"
end
end
end
我不确切知道我需要确保哪些查询没有重叠。非常感谢任何帮助
答案 0 :(得分:1)
我不使用Arel,但我认为查询应该是:
constraints = arel_table[:started_at].lteq(ended_at)
.and(arel_table[:ended_at].gteq(started_at))
时两个时段重叠
period1.start < period2.end
period1.end > period2.start
答案 1 :(得分:0)
您可以使用它代替代码或从中获取condition code
# Return the condition string depend on exclude_edges option.
def condition_string(starts_at_attr, ends_at_attr)
except_option = Array(options[:exclude_edges]).map(&:to_s)
starts_at_sign = except_option.include?(starts_at_attr.to_s.split(".").last) ? "<" : "<="
ends_at_sign = except_option.include?(ends_at_attr.to_s.split(".").last) ? ">" : ">="
query = []
query << "(#{ends_at_attr} IS NULL OR #{ends_at_attr} #{ends_at_sign} :starts_at_value)"
query << "(#{starts_at_attr} IS NULL OR #{starts_at_attr} #{starts_at_sign} :ends_at_value)"
query.join(" AND ")
end
答案 2 :(得分:0)
我会构建一个看起来像这样的查询:
Event.exists?( 'started_at < ? AND ended_at > ?', ended_at, started_at )
如果返回true,则存在重叠记录。