【问题标题】:Rails validate uniqueness of date rangesRails 验证日期范围的唯一性
【发布时间】:2013-04-27 13:01:28
【问题描述】:

我有一个涉及员工缺勤记录的应用程序。

我需要确保每条记录的开始日期和结束日期不重叠。

因此,例如,如果我输入了今天开始并明天结束的缺勤记录,则不应以任何方式输入该日期范围内的另一个缺勤记录。所以我无法制作一个从前一天开始,然后在后天或任何更晚的日期结束。

简单地说,我需要让日期范围独一无二。

实现这一目标的最佳方法是什么?

模型类中涉及遍历所有记录的自定义验证器需要很长时间才能完成,而且我还没有找到任何解决此问题的 gem。我也没有找到任何简单的方法来通过模型中的唯一性来确定范围。我被难住了:/

感谢您的宝贵时间!

编辑:

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

【问题讨论】:

  • 可能通过创建模型来记录使用日期?像 EmployeeAbsenceDate 之类的东西。这比遍历所有记录要快。

标签: ruby-on-rails validation


【解决方案1】:

我用这些:

  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

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

【讨论】:

  • 你能解释一下你在前面的例子中使用的那些兄弟姐妹是什么吗?以及如何实际使用您的重叠范围?仅通过重叠范围验证两个日期的唯一性?
  • 您是否希望给定用户在给定时间段内不超过一次缺勤?假设这个会更新。
  • 是的,应该只允许每个用户从 2013 年 1 月 1 日到例如 2013 年 10 月 1 日的缺勤记录。对于该用户,这些日期之间的所有日期都禁止用于任何其他缺勤记录。
  • 我得到一个“未定义的方法‘键?’对于 nil:NilClass" 错误和 "您需要在 "validates :not_overlap" 上提供至少一个验证。顺便说一句,兄弟姐妹到底是什么?如果我知道的话,我可能会做出更多贡献。我远非范围界定专家,哈!编辑:啊,我看到你在新代码中定义它。干杯!
  • user.absences.where.not(id: id) 会生成正确的语句,不需要-1。不过需要 Rails 4。
【解决方案2】:

作为对已接受答案的修改,这里有一个重叠范围,适用于不理解 DATEDIFF 的数据库

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

这借鉴了Determine Whether Two Date Ranges Overlap的解决方案

【讨论】:

  • 老实说,使用&gt;=&lt;= 似乎比DATEDIFF 更具可读性和可移植性,即使您的数据库确实支持DATEDIFF
【解决方案3】:

使用宝石validates_overlap

gem 'validates_overlap'

例如,如果您有一个名为Meeting 的模型具有start_dateend_date 类型为date 的字段,您可以轻松验证它们不重叠。

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

另一个更现实的例子,比如Meetingbelongs_to aUser,您可以将其范围扩大,因此它只验证特定用户的会议。

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

【讨论】:

    【解决方案4】:

    有一个名为 validates_overlap 的 gem,可让您轻松验证日期范围重叠。您还可以在验证中使用范围。

    【讨论】:

      【解决方案5】:

      虽然 juanpastas 解决方案是正确的,但它对创建记录有效,但可能导致更新时出现错误的否定验证。

      如果您需要编辑现有记录,假设范围是 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
      

      将消除这个缺点。 (也应遵循 Dave T 的添加,因为它与 DB 无关。)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2023-03-07
        • 1970-01-01
        • 1970-01-01
        • 2018-06-24
        • 1970-01-01
        • 2013-05-01
        • 1970-01-01
        相关资源
        最近更新 更多