【问题标题】:What's the correct syntax for remove_index in a Rails 3.1.0 migration?Rails 3.1.0 迁移中 remove_index 的正确语法是什么?
【发布时间】:2012-02-20 03:39:42
【问题描述】:

我正在将 Devise 添加到现有的 Rails 应用程序中,并且已经定义了一个用户表。设计生成器推出了以下迁移:

class AddDeviseToUsers < ActiveRecord::Migration
  def self.up
    change_table(:users) do |t|

     ## Database authenticatable
     t.string :email,              :null => false, :default => ""
     t.string :encrypted_password, :null => false, :default => ""

     ## Recoverable
     t.string   :reset_password_token
     t.datetime :reset_password_sent_at

     ## Rememberable
     t.datetime :remember_created_at

     ## Trackable
     t.integer  :sign_in_count, :default => 0

     blah blah blah....

   end

   add_index :users, :email,                :unique => true
   add_index :users, :reset_password_token, :unique => true
 end

没有生成向下迁移,我在删除这些索引时花了很多时间。我在文档中看到了不同的建议符号,以及在线不同的建议,但它们似乎都不适合我。比如……

def self.down
  change_table(:users) do |t|
    t.remove  :email
    t.remove  :encrypted_password

    t.remove  :reset_password_token

    blah blah blah...
  end

  remove_index :users, :email
  remove_index :users, :reset_password_token
end

结果...

An error has occurred, this and all later migrations canceled:

Index name 'index_users_on_email' on table 'users' does not exist

这很奇怪,因为如果我检查数据库,果然,'index_users_on_email' 就在那里......

我尝试过其他变体,包括

remove_index :users, :column => :email

remove_index :users, 'email'

或:

change_table(:users) do |t|
  t.remove_index :email
end

...但没有骰子。我正在使用 Postgres 运行 Rails 3.1.0、Ruby 1.9.2、rake 0.9.2.2。

让我失望的命令是:

bundle exec rake db:rollback STEP=1

成功应用迁移后。有什么建议吗?

【问题讨论】:

  • 还记得先删除索引,然后删除列,您的代码会反其道而行之,即使您使用正确的语法也会失败。

标签: ruby-on-rails ruby-on-rails-3.1 rails-migrations


【解决方案1】:

根据数据库类型,您无需担心删除self.down 方法中的索引,因为删除列时索引会自动从数据库中删除。

您也可以在 self.down 方法中使用此语法:

def self.down
   remove_column :users, :email
   remove_column :users, :encrypted_password
   remove_column :users, :reset_password_token
end

【讨论】:

  • 根据这个问题的答案,删除列时不会删除索引:stackoverflow.com/questions/7204476/…
  • ^ 取决于数据库类型
  • 为了进一步@iwasrobbed 的响应,请谨慎使用这种方法。如果你有一个复合索引并且只删除 MySQL 上的一列,它仍然会留下索引。它只会引用剩余的列。下面的响应是一种更安全的显式删除索引的方法。
【解决方案2】:

这是我的完整版本(在 Rails 5 中):

我将 team_id 作为表供应商的索引。我不再需要这种关系。摆脱它。做了以下事情:

1) 创建迁移。

  $ rails generate migration RemoveTeam_idFromVendor team_id:integer

2) 运行迁移,给我这个错误。那是因为 vendor 表中有行的外键引用了 team 表的主键值

== 20170727202815 RemoveTeamIdFromVendor: migrating ===========================
-- remove_column(:vendors, :team_id, :integer)
rake aborted!
StandardError: An error has occurred, this and all later migrations canceled:

SQLite3::ConstraintException: FOREIGN KEY constraint failed: DROP TABLE "vendors"

3) 为了解决这个问题并让迁移运行,我做了以下操作(注意:我在开发中):

$ rake db:drop


Dropped database 'db/development.sqlite3'
Dropped database 'db/test.sqlite3'


$ rake db:create
Created database 'db/development.sqlite3'
Created database 'db/test.sqlite3'

$ rake db:migrate
~
~
~

== 20170727202815 RemoveTeamIdFromVendor: migrating ===========================
-- remove_column(:vendors, :team_id, :integer)
   -> 0.0185s
== 20170727202815 RemoveTeamIdFromVendor: migrated (0.0185s) ==================

【讨论】:

    【解决方案3】:

    要更改表和/或其索引,请在迁移的 #change 操作中使用 #change_table。然后您可以创建可逆索引删除,如下所示:

    def change
       change_table :users do |t|
          t.index :email, :unique => true
          t.index :reset_password_token, :unique => true
       end
    end
    

    当您必须使用可逆操作删除带有索引的表时,您可以使用 #drop_table 方法来处理 SchemaStatements#index 类的 Table 方法 ConnectionAdapter

    def change
       drop_table :users do |t|
          t.index :email, :unique => true
          t.index :reset_password_token, :unique => true
       end
    end
    

    如果您在迁移中恰好需要 #up/down 对。对于ConnectionAdapter,只使用#change_table 方法和Table 类的#remove_index 方法:

    def up
       change_table :users do |t|
          t.index :email, :unique => true
          t.index :reset_password_token, :unique => true
       end
    end
    
    def down
       change_table :users do |t|
          t.remove_index :email, :unique => true
          t.remove_index :reset_password_token, :unique => true
       end
    end
    

    所有方法都在Rails 版本的2.1.0 或更早版本中可用。

    【讨论】:

      【解决方案4】:

      我想扩展@iWasRobbed 的答案。如果您仅在单个列上有索引,那么担心remove_index 是没有意义的,因为(只是一个假设!)数据库应该足够聪明,可以清理该索引使用的资源。但是,如果您有多个列索引,则删除该列会将索引减少到仍然存在的列,这是完全明智的做法,但有点显示您可能想要明确使用 remove_index 的位置。

      仅用于说明 - 下面的迁移存在这样一个缺陷,即在上下应用后,它会在 email 上留下唯一索引(这意味着 down 部分无法正常工作)

      class AddIndexes < ActiveRecord::Migration
        def up
          add_column :users, :action_name, :string
          add_index  :users, [:email, :action_name], unique: true
        end
      
        def down
          remove_column :users, :action_name
        end
      end
      

      down 块更改为

        def down
          remove_index :users, [:email, :action_name]
          remove_column :users, :action_name
        end
      

      将修复该缺陷并允许迁移使用rake db:rollback 正确地将数据库返回到以前的状态

      【讨论】:

        【解决方案5】:

        您还可以删除指定列的索引,从我的角度来看,这比写名称更不容易出错

        remove_index :actions, :column => [:user_id, :action_name]
        

        【讨论】:

        • 对于问题,这将是remove_index :users, column: :email
        【解决方案6】:

        为了记录,按名称删除索引的方法是

        remove_index(:table_name, :name => 'index_name')
        

        在你的情况下

        remove_index(:users, :name => 'index_users_on_email')
        

        【讨论】:

        • 对于新语法的复制粘贴:remove_index :users, name: 'index_users_on_email'
        猜你喜欢
        • 2020-05-23
        • 1970-01-01
        • 2022-01-10
        • 1970-01-01
        • 2021-07-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-04-19
        相关资源
        最近更新 更多