Rails validate uniqueness of date ranges

前端 未结 5 964
情深已故
情深已故 2021-02-05 23:33

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,

相关标签:
5条回答
  • 2021-02-05 23:37

    As a modification to the accepted answer, here's an overlaps scope that will work for DBs that don't understand DATEDIFF

      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

    0 讨论(0)
  • 2021-02-05 23:52

    There is a gem called validates_overlap which allows you to easily validate date range overlaps. You can also use scopes on the validation.

    0 讨论(0)
  • 2021-02-05 23:54

    Use the 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.

    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.

    class Meeting < ActiveRecord::Base
      belongs_to User
      validates :start_date, :end_date, overlap: { scope: 'user_id',
                                                 message_content: 'overlaps with Users other meetings.' }
    end
    
    0 讨论(0)
  • 2021-02-05 23:57

    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

    0 讨论(0)
  • 2021-02-06 00:02

    While juanpastas solution is correct, it will be valid for creation of records, but can lead to false negative validations on updates.

    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.

      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.)

    0 讨论(0)
提交回复
热议问题