2013-04-27 14 views
9

Ho un'applicazione che comporta record di assenza per i dipendenti.Rails convalida l'univocità degli intervalli di date

Ho bisogno di garantire che le date di inizio e di fine di ogni record non si sovrappongano.

Ad esempio, se inserissi un record di assenza iniziato oggi e terminato domani, non dovrebbe essere possibile inserire un altro all'interno di tale intervallo di date in alcun modo. Quindi non ho potuto crearne uno che inizi il giorno prima di oggi, per poi terminare dopodomani o in una data successiva.

Per dirla in parole semplici, ho bisogno di rendere unico l'intervallo di date.

Qual è il modo migliore per raggiungere questo obiettivo?

I validatori personalizzati nella classe del modello che coinvolgono l'iterazione di tutti i record impiegano troppo tempo per essere completati e non ho trovato gemme che risolvono questo problema. Nemmeno io ho trovato alcun modo semplice di dare uno sguardo all'unicità nel modello. Sono perplesso:/

Grazie per il vostro tempo!

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

Forse creando un modello per registrare le date utilizzate? Qualcosa come EmployeeAbsenceDate. Questo sarebbe più veloce di iterare su tutti i record. – Edward

risposta

18

io uso questi:

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 

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

+0

Puoi spiegare che cosa erano quei fratelli che hai usato nell'esempio precedente? E come si fa a utilizzare effettivamente l'ambito di sovrapposizioni? Solo convalidando l'unicità delle due date dall'ambito delle sovrapposizioni? – Marc

+0

non si desidera più di una assenza in un determinato periodo per un determinato utente? andando ad aggiornare assumendo questo. – juanpastas

+0

Sì, a ciascun utente deve essere consentito un record di assenza dall'1/1/2013 a, ad esempio, il 10/1/2013. Tutte le date comprese tra quelle date sono off-limits per qualsiasi altro record di assenza, per quell'utente. – Marc

8

come modifica la risposta accettata, ecco un ambito sovrapposizioni che lavorerà per DB che don Capisco DATEDIFF

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

Questo attinge la soluzione per Determine Whether Two Date Ranges Overlap

+2

onestamente, usando '> =' e '<=' sembra più leggibile e portatile di 'DATEDIFF' anche se il tuo DB supporta' DATEDIFF'. –

1

V'è un gioiello chiamato validates_overlap che permette di convalidare facilmente intervallo di date si sovrappone. È inoltre possibile utilizzare gli ambiti per la convalida.

1

Mentre la soluzione juanpastas è corretta, sarà valida per la creazione di record, ma può portare a convalide false negative sugli aggiornamenti.

Se è necessario modificare un record esistente, dire che l'intervallo è 2014-03-13..2014-06-12 e si desidera ridurlo a 2014-03-13..2014-04-12, si Riceverà un errore di sovrapposizione perché sta controllando contro se stesso.

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

ovvierà a tale mancanza. (anche l'aggiunta di Dave T deve essere seguita, essendo DB-agnostica.)

8

Utilizzare la gemma validates_overlap.

gem 'validates_overlap' 

Ad esempio, se si dispone di un modello chiamato Meeting con start_date e end_date campi di tipo date, si può facilmente verificare che non si sovrappongano.

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

Un altro esempio più realistico, dire una Meeting belongs_to un User, è possibile portata fuori, in modo che convalida solo incontri per un particolare utente.

Problemi correlati