39

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

4

6 に答える 6

53

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
于 2011-01-12T11:14:17.240 に答える
27

Rails6.1+チェック制約

Rails 6.1は、データベース移行へのチェック制約の基本的なサポートを追加しました。

したがって、整数列の値を1、2、および3のみに制限するチェック制約を追加するための移行は、次のように記述できます。

class AddConstraint < ActiveRecord::Migration
  def up
    add_check_constraint :table_name, 'check_column_name IN (1, 2, 3)', name: 'check_constraint_name'
  end

  def down
    remove_check_constraint :table_name, name: 'check_constraint_name'
  end
end

とに関する詳細を確認できる相対PRへのリンクは次のとおりです。add_check_constraintremove_check_constraint

于 2020-08-05T19:37:08.743 に答える
5

MigrationValidatorsgemでそれを行うことができます。詳細はこちらをご覧ください: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"]
于 2015-01-28T16:36:33.810 に答える
5

この回答は2021年5月の時点で廃止されています

このための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のみがサポートされていることに注意してください。

于 2017-03-17T13:49:49.073 に答える
4

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

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

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

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

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

于 2012-03-19T00:23:14.940 に答える
2

Sequelgemhttps ://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
于 2016-03-20T22:52:05.160 に答える