2016-06-15 11 views
0

私はModelフィールドを持つRailsアプリケーションを持っています。データベースでは、このフィールドには固有の制約が設定されています。 モデルレベルでは、uniqueness: { case_sensitive: false }, on: :createチェックも行っています。検証が失敗した場合にActiveRecordトランザクションをロールバックしない方法

私の考えは、ActiveRecord検証が行われた後、一意性チェックが失敗した場合、同じテーブル内の既存のすべてのレコードのカウンタをインクリメントしたいと考えています。だから今私がしていることはこれです:

after_validation :term_exists 

    private 
    def term_exists 
     if self.errors.added? :name, :taken 
      Term.where('LOWER(name) = LOWER(?)', self[:name]).update_all('popularity_counter = popularity_counter + 1') 
     end 
    end 

これは素晴らしいですが、私はそれに対してテストを実行すると、カウンタは増分しません。私は、この理由は、検証に失敗した場合にトランザクション内のすべてがロールバックされるということだと思います。

しかし、この種の問題を解決するにはどうすればよいでしょうか?このようなロジックをコントローラに保存しないことを強くお勧めします。

答えて

1

私はあなたの名前が一意であるかどうかをチェックし、そうでなければあなたのカウンターを更新してから検証を失敗するカスタムbefore_validationメソッドを作成する必要があると言います。

この場合、明らかにあなたが望んでいないので、uniqueness: { case_sensitive: false }, on: :createを削除することができます。

私はそれが私の頭に浮かぶ最もクリーンな方法だと思います。

+0

しかし、検証に失敗すると、トランザクションもロールバックされますか? – Kaspar

+1

私はそれについては分かりません。ロールバックはコミットがあるときに実行されますが、まだコミットを取得していません。あなたは妥当性確認段階の直前です。そして、ところで、Railsにはafter_rollbackコールバックがあるので、検証中にフラグを設定し、そのフラグをafter_rollbackコールバックで使用してカウンタを更新することができます。いずれか他の方法で動作します。あなたはそれらを試し、何が起きたらログをチェックする必要があります。私は頭の上から覚えていない。 –

+0

Allrightは、実験して受け入れる答えを見ていきます。ありがとう! – Kaspar

1

MySQLを使用している場合は、代わりにINSERT ... ON DUPLICATE KEY UPDATE ...the docsを参照)を試すことができます。これを行うには、name列のユニークなインデックスを追加する必要があります。このシナリオでは、一意性検証は必要ありません。

upsertという有望な宝石もありますが、これは挿入された値ではなく、別の列(カウンタなど)の更新をまだサポートしていません。あなたはthis issueを見ることができます。

+0

PostgreSQLの使用問題を解決するには少し違いがあるようです。 編集:また、私は私のモデルのINSERTをオーバーライドする必要がありますか? – Kaspar

+0

はい、新しいPostgresのバージョンでは、代わりに 'INSERT ... ON CONFLICT ...'句があるようです([docs](https://www.postgresql.org/docs/current/static/sql-insert.html## SQL-ON-CONFLICT))。 – BoraMa

関連する問題