web-dev-qa-db-ja.com

Rails移行でチェック制約を追加するにはどうすればよいですか?

Railsアプリの既存のテーブルに新しい整数列を追加する必要があります。列には値1、2、3しか持てないので、チェック制約を追加したいと思います。テーブル/列。Rails移行内でこの制約を指定するにはどうすればよいですか?

30
Jeff

Railsの移行では、制約を追加する方法は提供されませんが、実際のSQLをexecute()に渡すことで、移行を介して追加できます。

移行ファイルの作成:

Ruby script/generate Migration AddConstraint

さて、移行ファイルでは:

class AddConstraint < ActiveRecord::Migration
  def self.up
    execute "ALTER TABLE table_name ADD CONSTRAINT check_constraint_name CHECK (check_column_name IN (1, 2, 3) )"
  end

  def self.down
    execute "ALTER TABLE table_name DROP CONSTRAINT check_constraint_name"
  end
end
44
Nilesh

Migration Validatorsgemでそれを行うことができます。詳細はこちらをご覧ください: https://github.com/vprokopchuk256/mv-core

そのgemを使用すると、dbレベルで包含検証を定義できます。

def change
  change_table :table_name do |t|
    t.integer :column_name, inclusion: [1, 2, 3]
  end
end

さらに、その検証をどのように定義するか、さらには表示する必要のあるエラーメッセージを定義することができます。

def change
  change_table :posts do |t|
    t.integer :priority, 
              inclusion: { in: [1, 2, 3], 
                           as: :trigger, 
                           message: "can't be anything else than 1, 2, or 3" }
  end
end

その検証を、移行からモデルに直接レベルアップすることもできます。

class Post < ActiveRecord::Base 
  enforce_migration_validations
end

次に、移行で定義された検証は、モデルでのActiveModel検証としても定義されます。

Post.new(priority: 3).valid? 
=> true

Post.new(priority: 4).valid?
=> false

Post.new(priority: 4).errors.full_messages
=> ["Priority can't be anything else than 1, 2, or 3"]
3

PostgreSQLCHECK制約を機能させる作業を行ったところです。

Nileshのソリューションは完全ではありません。 db/schema.rbファイルには制約が含まれないため、db:setupを使用するテストおよびデプロイメントは制約を取得しません。 http://guides.rubyonrails.org/migrations.html#types-of-schema-dumps による

移行中にカスタムSQLステートメントを実行できますが、スキーマダンパーはデータベースからそれらのステートメントを再構成できません。このような機能を使用している場合は、スキーマ形式を:sqlに設定する必要があります。

つまり、config /application.rbセット内

config.active_record.schema_format = :sql

残念ながら、PostgreSQLを使用している場合、結果のダンプをロードするときにエラーが発生する可能性があります。 エラー:言語plpgsqlの所有者である必要があります の説明を参照してください。その議論では、PostgreSQLの構成パスをたどりたくありませんでした。さらに、いずれにせよ、私は読み取り可能なdb /schema.rbファイルを持っているのが好きです。そのため、移行ファイル内のカスタムSQLは除外されました。

https://github.com/vprokopchuk256/mv-core Valeraによって提案されたgemは有望なようですが、サポートされる制約のセットは限られています(そして、使用しようとするとエラーが発生しました。それは私が含めている他の宝石との非互換性が原因かもしれませんが)。

私が行った解決策(ハック)は、モデルコードに制約を挿入させることです。それは一種の検証のようなものなので、私はそれを置きます:

class MyModel < ActiveRecord::Base

    validates :my_constraint

    def my_constraint
        unless MyModel.connection.execute("SELECT * FROM information_schema.check_constraints WHERE constraint_name = 'my_constraint'").any?
            MyModel.connection.execute("ALTER TABLE my_models ADD CONSTRAINT my_constraint CHECK ( ...the SQL expression goes here ... )")
        end
    end

もちろん、これは各検証の前に追加の選択を行います。それが問題である場合の解決策は、 railsを使用してOracleに接続した後に特定のスクリプトを実行する方法 (単純にキャッシュすることはできません)で説明されているような「接続後」のモンキーパッチに配置することです。検証/制約の追加はロールバックされる可能性のあるトランザクション内で発生するため、選択の結果。毎回確認する必要があります。)

3
ronen

このためのgemを公開しました: active_record-postgres-constraints[〜#〜] readme [〜#〜] で説明されているように、db/schema.rbファイルで使用でき、移行で次のメソッドのサポートが追加されます。

create_table TABLE_NAME do |t|
  # Add columns
  t.check_constraint conditions
  # conditions can be a String, Array or Hash
end

add_check_constraint TABLE_NAME, conditions
remove_check_constraint TABLE_NAME, CONSTRAINT_NAME

現時点では、postgresのみがサポートされていることに注意してください。

3
Isaac Betesh

Sequel gem https://github.com/jeremyevans/sequel を使用できます

Sequel.migration do
  change do
    create_table(:artists) do
      primary_key :id
      String :name
      constraint(:name_min_length){char_length(name) > 2}
    end
  end
end
0
John Doe