简体   繁体   English

Rails验证日期范围的唯一性

[英]Rails validate uniqueness of date ranges

I have an application that involves absence records for employees. 我有一个涉及员工缺勤记录的申请。

I need to ensure that the start and end dates for each record don't overlap. 我需要确保每条记录的开始和结束日期不重叠。

So for example, if I entered an absence record that started today and ended tomorrow, it shouldn't be possible to enter another inside of that date range in any way. 因此,例如,如果我输入了今天开始并明天结束的缺勤记录,则不应该以任何方式在该日期范围内输入另一个记录。 So I couldn't make one that starts the day before today, then ends the day after tomorrow, or any later date. 所以我不能制作一个在今天前一天开始,然后在后天或任何更晚的日期结束。

To put it simply, I need to make the date range unique. 简而言之,我需要使日期范围独一无二。

What is the best way to achieve this? 实现这一目标的最佳方法是什么?

Custom validators in the model class that involve iterating through all records take far too long to complete, and I haven't found any gems that address this problem. 模型类中涉及遍历所有记录的自定义验证器需要很长时间才能完成,而且我没有找到任何解决此问题的宝石。 I also haven't found any simply way to scope by uniqueness in the model either. 我也没有找到任何简单的方法来确定模型中的唯一性。 I'm stumped :/ 我很难过:/

Thanks for your time! 谢谢你的时间!

Edit: 编辑:

class Absence < ActiveRecord::Base
attr_accessible :date, :date_ended, :status, :reason, :form, :user_id, :tempuser, :company_id
belongs_to :user
default_scope { where(company_id: Company.current_id) }

validates :date, :date_ended, :status, :reason, :form, :user_id, presence: true 
validates_numericality_of :user_id, :only_integer => true, :message => "can only be whole number."
end

I use these: 我用这些:

  scope :overlaps, ->(start_date, end_date) do
    where "(DATEDIFF(start_date, ?) * DATEDIFF(?, end_date)) >= 0", end_date, start_date
  end

  def overlaps?
    overlaps.exists?
  end

  # Others are models to be compared with your current model
  # you can get these with a where for example
  def overlaps
    siblings.overlaps start_date, end_date
  end

  validate :not_overlap

  def not_overlap
    errors.add(:key, 'message') if overlaps?
  end

  # -1 is when you have a nil id, so you will get all persisted user absences
  # I think -1 could be omitted, but did not work for me, as far as I remember
  def siblings
    user.absences.where('id != ?', id || -1)
  end

Source: https://makandracards.com/makandra/984-test-if-two-date-ranges-overlap-in-ruby-or-rails 资料来源: https//makandracards.com/makandra/984-test-if-two-date-ranges-overlap-in-ruby-or-rails

As a modification to the accepted answer, here's an overlaps scope that will work for DBs that don't understand DATEDIFF 作为对已接受答案的修改,这里是一个重叠范围,适用于不理解DATEDIFF的DB

  scope :overlaps, ->(start_date, end_date) do
    where "((start_date <= ?) and (end_date >= ?))", end_date, start_date
  end

This draws on the solution for Determine Whether Two Date Ranges Overlap 这借鉴了确定两个日期范围是否重叠的解决方案

Use the gem validates_overlap . 使用gem validates_overlap

gem 'validates_overlap'

For example, if you have a model called Meeting with a start_date and end_date fields of type date , you can easily validate that they don't overlap. 例如,如果您有一个名为Meeting的模型,其start_dateend_date字段的类型为date ,则可以轻松验证它们是否重叠。

class Meeting < ActiveRecord::Base
  validates :start_date, :end_date, overlap: true 
end

Another more realistic example, say a Meeting belongs_to a User , you can scope it out, so it only validates meetings for a particular user. 另一个更现实的例子,比如Meeting属于User ,您可以将其范围化,因此它仅验证特定用户的会议。

class Meeting < ActiveRecord::Base
  belongs_to User
  validates :start_date, :end_date, overlap: { scope: 'user_id',
                                             message_content: 'overlaps with Users other meetings.' }
end

There is a gem called validates_overlap which allows you to easily validate date range overlaps. 有一个名为validates_overlap的gem,可以让您轻松验证日期范围重叠。 You can also use scopes on the validation. 您还可以在验证中使用范围。

While juanpastas solution is correct, it will be valid for creation of records, but can lead to false negative validations on updates. 虽然juanpastas解决方案是正确的,但它对于创建记录是有效的,但可能导致对更新的错误否定验证。

If you need to edit an existing record, say the range is 2014-03-13..2014-06-12 and you want to reduce it to 2014-03-13..2014-04-12, you'll get an overlap error because it is checking AGAINST itself. 如果您需要编辑现有记录,请说范围是2014-03-13..2014-06-12并且您想将其缩小到2014-03-13..2014-04-12,您将得到一个重叠错误,因为它正在检查AGAINST本身。

  def siblings
    Absence_users.where('user_id = ? AND id != ?', user_id, self)
  end

will obviate that shortcoming. 将消除这个缺点。 (Dave T's addition should also be followed, being DB-agnostic.) (也应该遵循Dave T的添加,与DB无关。)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM