检查Ruby on Rails中的日期重叠

时间:2016-02-11 14:42:45

标签: sql ruby-on-rails ruby

我的模型有两列(started_atended_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

我不确切知道我需要确保哪些查询没有重叠。非常感谢任何帮助

3 个答案:

答案 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)

查看Validates Overlap gem

您可以使用它代替代码或从中获取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,则存在重叠记录。