2013-04-27 6 views
9

У меня есть приложение, которое включает в себя записи отсутствия сотрудников.Rails проверяет уникальность диапазонов дат

Мне нужно, чтобы начальная и конечная даты для каждой записи не пересекались.

Так, например, если я ввел запись об отсутствии, которая началась сегодня и закончилась завтра, не должно быть возможности ввести другую внутри этого диапазона дат любым способом. Поэтому я не мог сделать тот, который начинается накануне сегодня, а затем заканчивается послезавтра или позже.

Проще говоря, мне нужно сделать уникальный диапазон дат.

Каков наилучший способ достичь этого?

Пользовательские валидаторы в классе модели, которые связаны с итерированием по всем записям, занимают слишком много времени, и я не нашел камней, которые решают эту проблему. Я также не нашел простого способа охвата по уникальности в модели. Я в тупике:/

Спасибо за ваше время!

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 
+0

Возможно, создав модель для записи использованных дат? Что-то вроде EmployeeAbsenceDate. Это будет быстрее, чем повторение всех записей. – Edward

ответ

18

Я использую эти:

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

+0

Не могли бы вы объяснить, что эти братья и сестры были использованы в предыдущем примере? И как вы идете на самом деле, используя свою область перекрытий? Просто подтвердив уникальность двух дат по объему перекрытий? – Marc

+0

Вы хотите не более одного отсутствия в данный период для данного пользователя? собираюсь обновиться, предполагая это. – juanpastas

+0

Да, каждому пользователю должна быть разрешена запись об отсутствии с 1/1/2013 до, например, 10/1/2013. Все даты между этими датами являются вне пределов для любых других записей отсутствия для этого пользователя. – Marc

8

В качестве модификации к принятому ответ, вот перекрывается сфера, которая будет работать для блоков данных, которые дон 't понять 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

+2

честно, используя '> =' и '<=' кажется более читабельным и переносимым, чем 'DATEDIFF', даже если ваша БД поддерживает' DATEDIFF'. –

1

Существует драгоценный камень называется validates_overlap, который позволяет легко проверить диапазон дат перекрывается. Вы также можете использовать области действия для проверки.

1

Хотя решение 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-агностиком.)

8

Используйте драгоценный камень validates_overlap.

gem 'validates_overlap' 

Например, если у вас есть модель под названием Meeting с start_date и end_date полей типа date, вы можете легко проверить, что они не перекрывают друг друга.

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

Еще более реалистичный пример, скажем Meeting belongs_to 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 
Смежные вопросы