2013-04-27 11 views
9

Ich habe eine Anwendung, die Abwesenheitsdatensätze für Angestellte enthält.Schienen validieren Eindeutigkeit der Datumsbereiche

Ich muss sicherstellen, dass die Anfangs- und Enddaten für jeden Datensatz nicht überlappen.

Wenn ich zum Beispiel einen Abwesenheitsdatensatz eingegeben habe, der heute begann und morgen endete, sollte es nicht möglich sein, einen anderen innerhalb dieses Datumsbereichs einzugeben. Also konnte ich keinen machen, der am Vortag beginnt und dann übermorgen oder später ausläuft.

Um es einfach auszudrücken, muss ich den Datumsbereich einzigartig machen.

Was ist der beste Weg, dies zu erreichen?

Benutzerdefinierte Validatoren in der Modellklasse, bei denen alle Datensätze durchlaufen werden, sind viel zu lang und ich habe keine Edelsteine ​​gefunden, die dieses Problem beheben. Ich habe auch keinen einfachen Weg gefunden, um durch die Einzigartigkeit im Modell zu erreichen. Ich bin ratlos:/

Danke für Ihre Zeit!

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

Möglicherweise durch Erstellen eines Modells zum Aufzeichnen verwendeter Daten? So etwas wie EmployeeAbsenzDate. Dies wäre schneller als das Iterieren aller Datensätze. – Edward

Antwort

18

Ich benutze diese:

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 

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

+0

Können Sie erklären, welche dieser Geschwister Sie im vorherigen Beispiel verwendet haben? Und wie geht man eigentlich mit dem Überlappungsbereich um? Nur indem die Eindeutigkeit der beiden Daten durch den Überlappungsbereich überprüft wird? – Marc

+0

möchten Sie nicht mehr als eine Abwesenheit in einem bestimmten Zeitraum für einen bestimmten Benutzer? gehen zu aktualisieren unter der Annahme, dass dies. – juanpastas

+0

Ja, jeder Benutzer sollte nur eine Abwesenheitsnotiz vom 01.01.2013 bis zum 01.10.2013 haben. Alle Daten zwischen diesen Daten sind für alle anderen Abwesenheitsdatensätze für diesen Benutzer gesperrt. – Marc

8

Als eine Modifikation der akzeptierte Antwort, hier ist ein Überlappungen Rahmen, die für die DBs arbeiten, dass Don nicht verstehen DATEDIFF

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

Diese stützt sich auf die Lösung für Determine Whether Two Date Ranges Overlap

+2

ehrlich gesagt, mit '> =' und '<=' scheint lesbarer und portabler als 'DATEDIFF', auch wenn Ihre DB' DATEDIFF' unterstützt. –

1

Es ist ein Juwel validates_overlap genannt ist, die Sie Datumsbereich überlappt leicht validieren können. Sie können Bereiche auch für die Validierung verwenden.

1

Während juanpastas Lösung korrekt ist, wird es für die Erstellung von Datensätzen gültig sein, kann aber zu falsch negativen Validierungen bei Updates führen.

Wenn Sie einen vorhandenen Datensatz bearbeiten müssen, sagen Sie den Bereich 2014-03-13..2014-06-12 und Sie möchten ihn auf 2014-03-13..2014-04-12 reduzieren Ich bekomme einen Überlappungsfehler, weil GTAINST sich selbst überprüft.

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

wird dieses Manko vermeiden. (Die Zugabe von Dave T sollte ebenfalls befolgt werden, da sie DB-agnostisch ist.)

8

Verwenden Sie den Edelstein validates_overlap.

gem 'validates_overlap' 

Zum Beispiel, wenn Sie ein Modell haben Meeting mit start_date und end_date Feldern vom Typ date genannt, können Sie leicht überprüfen, dass sie sich nicht überlappen.

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

Ein weiteres realistischeres Beispiel, sagen ein Meeting belongs_to eine User, können Sie es Rahmen aus, so ist es bestätigt nur Sitzungen für einen bestimmten Benutzer.

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