我需要在我的Rails应用程序中的现有表中添加一个新的整数列。该列只能有值1、2、3,因此我想在表/列中添加一个检查约束条件。如何在Rails迁移中指定此约束条件?
我需要在我的Rails应用程序中的现有表中添加一个新的整数列。该列只能有值1、2、3,因此我想在表/列中添加一个检查约束条件。如何在Rails迁移中指定此约束条件?
Rails迁移不提供添加约束的方法,但您仍然可以通过迁移来实现,只需通过execute()传递实际的SQL语句即可。
创建迁移文件:
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
因此,现在可以编写一个添加检查约束的迁移,该约束仅限于将整数列值限制为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
的更多详细信息。
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"]
我刚刚发布了一个针对此问题的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
rails app:update
时会引起麻烦。感谢@Isaac Betesh提供的宝石!它很有用。 - Masa Sakano也就是说,在config/application.rb中设置:虽然在迁移中可以执行自定义SQL语句,但模式转储程序无法从数据库中重建这些语句。如果您正在使用此类功能,则应将模式格式设置为:sql。
config.active_record.schema_format = :sql
不幸的是,如果您正在使用PostgreSQL,则在加载结果转储时可能会出现错误,请参见ERROR:必须是语言plpgsql的所有者的讨论。我不想在那个讨论中走下PostgreSQL配置路径;另外,在任何情况下,我都喜欢有一个可读的db/schema.rb文件。所以这就排除了在迁移文件中使用自定义SQL。
Valera建议的https://github.com/vprokopchuk256/mv-core gem似乎很有前途,但它只支持有限的约束集(当我尝试使用它时出现错误,但这可能是由于与我包含的其他gems不兼容所致)。
我采用的解决方案(hack)是使模型代码插入约束。由于它有点像验证,因此我将其放在那里:
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
您可以使用Sequel
宝石库 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
config/application.rb
中设置config.active_record.schema_format = :sql
,因为"db/schema.rb
无法表达特定于数据库的项目,如触发器、存储过程或检查约束条件。"。 - Paul Fioravantidb/schema.rb
中:active_record-postgres-constraints。更多细节请参见我的回答。目前仅支持postgres。 - Isaac Betesh