2013-04-27 26 views
9

従業員の不在記録を含むアプリケーションがあります。Railsは日付範囲の一意性を確認します

各レコードの開始日と終了日が重ならないようにする必要があります。

たとえば、今日始まって明日に終了した不在記録を入力した場合、その日付範囲の別の内部を決して入力することはできません。だから私は今日の前日から始まるものを作ることができず、明日の後の日かそれ以降の日付に終わるものを作ることができませんでした。

簡単に言えば、日付範囲を一意にする必要があります。

これを達成する最も良い方法は何ですか?

モデルクラスのカスタムバリデーターは、すべてのレコードを反復処理するのには時間がかかりますが、この問題に対処する宝石は見つかりませんでした。私はまた、モデルの一意性によって範囲を決める簡単な方法も見つけていません。私は困惑しています:/

ありがとう!

編集:

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

前の例で使用したそれらの兄弟は何でしたか説明できますか?実際にオーバーラップスコープを使用するにはどうしたらいいですか?オーバーラップスコープで2つの日付の一意性を検証するだけです。 – Marc

+0

あなたは特定のユーザーに対して一定期間内に複数の欠席を望みますか?これを前提に更新する予定です。 – juanpastas

+0

はい、各ユーザーには、2013年1月1日から1013年10月1日までの不在履歴のみを許可する必要があります。これらの日付の間にあるすべての日付は、そのユーザーの他の不在記録のために制限されていません。 – Marc

8

が、ここではそのドンのDBのために動作します重複範囲ですDATEDIFFを理解する

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

これは、あなたが簡単に日付範囲が重なって検証することができますvalidates_overlapと呼ばれる宝石がありDetermine Whether Two Date Ranges Overlap

+2

正直なところ、あなたのDBが 'DATEDIFF'をサポートしていても、'> = 'と' <= 'を使うと' DATEDIFF'より読みやすく可搬性が高いようです。 –

1

のためのソリューションを描画します。また、検証にスコープを使用することもできます。

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

gem validates_overlapを使用してください。あなたがタイプdatestart_dateend_dateフィールドでMeetingと呼ばれるモデルを持っている場合

gem 'validates_overlap' 

たとえば、あなたが簡単に彼らが重複していないことを検証することができます。

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

別のより現実的な例は、User belongs_toのMeeting、あなたはスコープそれを言うことができますので、それだけで特定のユーザーのための会議を検証します。

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