2016-07-19 9 views
0

User has_many Booksがあるとします。各書籍には名前欄があります。Railsに保存せずにフィールドの一意性を検証する方法は?

ユーザーは書籍を入力し、名前の配列としてアプリに送信されます。名前の配列は、既存の書籍を置き換えます。

更新が失敗した場合は、書籍を変更しないでください。

class Book 
    belongs_to :user 

    validates_uniquness_of :name, scope: [:user] 

保存しないで各書籍の有効性を確認するにはどうすればよいですか?例えば

['Rails Guide', 'Javascript for Dummies']が有効になります。

['Javascript for Dummies', 'Javascript for Dummies']は無効です。

params[:books].each{| b | Book.new(b).valid? }は、本を一意にするために保存する必要があるため動作しません。

Mongoid

答えて

1

あなたはActive Record Transactionを使用することができます。トランザクションを開始してsaveにコールし、失敗した場合はトランザクション全体がロールバックされます。たとえば、次のようになります。

Book.transaction do 
    params[:books].each{ |b| Book.new(b).save! } 
end 

例外が発生すると、トランザクション全体が中止されます。この場合は、ActiveRecord::RecordInvalidをキャッチして処理する必要があります。

+0

また、データベースに一意性制約が必要ですし、 'ActiveRecord :: RecordNotUnique'もキャッチする必要があります。 –

+0

これは良い方法ですが、Mongoにはトランザクションがありません。 –

1

Array#mapを使用すると、書籍の属性の配列を書籍の名前の配列に変換できます。あなたがデータベースに触れることなく、あなたのチェックを実行することができます

are_books_uniq = params[:books].map{|b| b[:name]}.uniq.size == params[:books].size 

この方法は:その後、ブック名の配列から重複を削除し、その後の本の元の配列は属性として結果の配列が同じサイズを持っているかどうかを確認するためにArray#uniqを使用しています。しかし、安全な側になるためには、すべての書籍を取引の中に保存する必要があります(@ Aaronのanswer参照)。

+0

検証ロジックを複製するので、この方法が嫌いでした。 –

1

これは私が想像していたよりはるかに複雑であることが判明しました。

私のようなルックスを思いついた解決策:複雑さのほとんどは、2機種のカップリングから来て

def update params 
    names = params.delete(:books) 

    new_books = names.map{| title | Book.new(name:name)} 
    validate_books_for new_books 

    return false if errors.present? 
    return false unless super(params) 

    self.books = new_books 

    self 
    end 

。なぜモデルを結合するのは良い考えではないのか分かります。おそらく、より良いデザインは、書籍を配列として格納することです。

関連する問題