Rails 迁移:检查存在并继续进行?

90

我在我的迁移中做了这样的事情:

add_column :statuses, :hold_reason, :string rescue puts "column already added"

但事实证明,尽管这对SQLite有效,但它对PostgreSQL无效。似乎如果add_column出现问题,即使捕获了异常,事务也会失败,因此迁移无法执行任何其他工作。

是否有任何非数据库特定的方法来检查列或表是否已存在?如果失败,是否有任何方法使我的异常处理块真正起作用?


需要提到的是,有条件的迁移会导致回滚问题,因为在回滚阶段不知道前向迁移期间的条件是什么。 - oklas
只在回滚的非可选部分执行。 - Dan Rosenstark
5个回答

193

从Rails 3.0版本开始,您可以使用column_exists?方法来检查某个列是否存在。

unless column_exists? :statuses, :hold_reason
  add_column :statuses, :hold_reason, :string
end

还有一个table_exists?函数,它可以追溯到Rails 2.1。


在添加/创建列或表之前检查其是否存在被认为是最佳实践吗?(当然,这取决于手头的问题) - Aldo 'xoen' Giambelluca
4
如果我在change方法中定义,这是否适用于回滚操作? - dardub
2
是的,回滚可能会成为一个问题...我们不确定是否应该删除该列...因为我们没有记录先前的状态。 - songyy

13

10
甚至可以更短。
add_column :statuses, :hold_reason, :string unless column_exists? :statuses, :hold_reason

这将是对其他答案的评论,而不是答案。谢谢。 - Dan Rosenstark

4

对于Rails 2.X,您可以使用以下方式检查列的存在:

columns("[table-name]").index {|col| col.name == "[column-name]"}

如果返回nil,则不存在这样的列。如果返回一个Fixnum,则该列存在。当然,如果您想通过更多选择性参数来识别列,例如仅通过名称来识别列,则可以在{...}之间放置更多参数。

{ |col| col.name == "foo" and col.sql_type == "tinyint(1)" and col.primary == nil }

(这篇回答最初发布在如何在Rails中编写条件迁移?

在Rails中,可以使用条件迁移来实现只在特定情况下才运行迁移。可以通过在迁移文件中嵌入Ruby代码来实现此目的。

例如,以下是一个示例迁移,仅在当前Rails环境为"production"时才执行:


class AddIndexToUsersEmail < ActiveRecord::Migration
  def change
    add_index :users, :email
    add_index :users, :username if Rails.env.production?
  end
end

在这个示例中,我们添加了一个索引到用户表的电子邮件列,并且仅当Rails环境为"production"时才添加另一个索引到用户名列。


0

unless Status.column_names.include?("hold_reason")add_column :statuses, :hold_reason, :string


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接