如何在Rails迁移中添加检查约束?

我需要在我的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 

我刚刚通过获得PostgreSQL CHECK约束来工作。

尼勒什的解决方案并不完整; db / schema.rb文件不包含约束,因此使用db:setup的测试和任何部署都不会获得约束。 根据http://guides.rubyonrails.org/migrations.html#types-of-schema-dumps

在迁移中,您可以执行自定义SQL语句,但架构转储程序无法从数据库重新构建这些语句。 如果您使用这样的function,那么您应该将架构格式设置为:sql。

即,在config / application.rb中设置

 config.active_record.schema_format = :sql 

不幸的是,如果您正在使用PostgreSQL,则在加载结果转储时可能会收到错误,请参阅ERROR中的讨论:必须是语言plpgsql的所有者 。 我不想在讨论中沿着PostgreSQL配置路径走下去; 在任何情况下,我都喜欢有一个可读的db / schema.rb文件。 因此,我在迁移文件中排除了自定义SQL。

Valera建议的https://github.com/vprokopchuk256/mv-core gem似乎很有希望,但它只支持一组有限的约束(当我尝试使用它时出现错误,尽管这可能是由于不兼容与其他gem我包括在内)。

我使用的解决方案(hack)是让模型代码插入约束。 因为它有点像validation,所以我把它放在哪里:

 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 

当然,这会在每次validation之前进行额外选择; 如果这是一个问题,解决方案是将它放在“连接后”猴子补丁中,如在使用rails连接到oracle后如何运行特定脚本中讨论的? (您不能简单地缓存select的结果,因为validation/约束添加发生在可能回滚的事务中,因此您需要每次都检查。)

您可以使用Migration Validators gem来完成。 详情请见: https : //github.com/vprokopchuk256/mv-core

使用该gem,您将能够在db级别定义包含validation:

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

此外,您还可以定义应如何定义validation,甚至可以显示应显示的错误消息:

 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 

您甚至可以升级从迁移到模型的validation:

 class Post < ActiveRecord::Base enforce_migration_validations end 

然后,迁移中的validation定义也将在模型中定义为ActiveModelvalidation:

 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"] 

我刚刚为此发布了一个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