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