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

时间:2011-01-12 01:18:44

标签: mysql ruby-on-rails ruby database-migration

我需要在Rails应用程序的现有表中添加一个新的整数列。该列只能有值1,2,3,所以我想在表/列中添加一个检查约束。如何在Rails迁移中指定此约束?

6 个答案:

答案 0 :(得分:43)

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

答案 1 :(得分:3)

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

Nilesh的解决方案并不完整; db / schema.rb文件不会包含约束,因此使用db:setup的测试和任何部署都不会获得约束。根据{{​​3}}

  

在迁移过程中,您可以执行自定义SQL语句   schema dumper无法从数据库重构这些语句。   如果您使用这样的功能,那么您应该设置架构   格式为:sql。

即,在config / application.rb中设置

config.active_record.schema_format = :sql

不幸的是,如果您正在使用PostgreSQL,则在加载生成的转储时可能会收到错误,请参阅http://guides.rubyonrails.org/migrations.html#types-of-schema-dumps处的讨论。我不想在那次讨论中沿着PostgreSQL配置路径走下去;在任何情况下,我都喜欢拥有可读的db / schema.rb文件。所以我在迁移文件中排除了自定义SQL。

Valera建议的ERROR: must be owner of language plpgsql宝石似乎很有希望,但它只支持一组有限的约束(当我尝试使用它时我遇到了错误,尽管这可能是由于与其他宝石的不兼容我和# 39; m包括)。

我使用的解决方案(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

当然,这会在每次验证之前进行额外选择;如果这是一个问题,解决方案就是将其放入&#34;连接后#34;如https://github.com/vprokopchuk256/mv-core中讨论的猴子补丁(你不能简单地缓存select的结果,因为验证/约束添加发生在可能被回滚的事务中,所以你需要每次检查。 )

答案 2 :(得分:3)

您可以使用Migration Validators gem执行此操作。详情请见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"]

答案 3 :(得分:3)

Rails 6.1+ Check Constraints

Rails 6.1 added basic support for check constraints to database migrations

因此,现在,用于添加仅将整数列值限制为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

a link to the relative PR处,您可以找到有关add_check_constraintremove_check_constraint的更多详细信息。

答案 4 :(得分:2)

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

答案 5 :(得分:0)

您可以使用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