Railsアプリの既存のテーブルに新しい整数列を追加する必要があります。列は1、2、3の値しか持てないので、テーブル/列にチェック制約を追加したいと思います。Railsの移行内でこの制約を指定するにはどうすればよいですか?
6 に答える
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
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_constraint
remove_check_constraint
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"]
この回答は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のみがサポートされていることに注意してください。
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に接続した後に特定のスクリプトを実行する方法で説明されているような「接続後」のモンキーパッチに配置することです。(検証/制約の追加はロールバックされる可能性のあるトランザクション内で行われるため、選択の結果を単純にキャッシュすることはできません。そのため、毎回チェックする必要があります。)
Sequel
gemhttps ://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