Какой правильный синтаксис для remove_index в миграции Rails 3.1.0?
Я пытаюсь добавить Devise к существующему Rails-приложению с уже определенной таблицей Users. Генератор проекта вытеснил следующую миграцию:
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
... но без кубиков. Я запускаю Rails 3.1.0, Ruby 1.9.2, rake 0.9.2.2, с Postgres.
Команда, которая меня отпускает:
bundle exec rake db:rollback STEP=1
после успешного применения миграции. Любые советы?
Ответы
Ответ 1
В зависимости от типа базы данных вам не нужно беспокоиться об удалении индексов в методе self.down
, поскольку индекс будет автоматически удален из базы данных при удалении столбца.
Вы также можете использовать этот синтаксис в своем методе self.down
:
def self.down
remove_column :users, :email
remove_column :users, :encrypted_password
remove_column :users, :reset_password_token
end
Ответ 2
Для записи способ удаления индекса по имени
remove_index(:table_name, :name => 'index_name')
поэтому в вашем случае
remove_index(:users, :name => 'index_users_on_email')
Ответ 3
Вы также можете удалить индекс, определяющий столбцы, которые с моей точки зрения меньше подвержены ошибкам, чем писать имя
remove_index :actions, :column => [:user_id, :action_name]
Ответ 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
исправит этот недостаток и позволит миграции правильно вернуть DB в предыдущее состояние с помощью rake db:rollback
Ответ 5
Чтобы изменить таблицу и/или ее индексы, используйте #change_table
внутри #change
действия миграции. Затем вы сможете создать обратимое удаление индекса следующим образом:
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
в переносе. Используйте только #change_table
вместе с #remove_index
метод класса Table
для ConnectionAdapter
:
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
или более ранних.
Ответ 6
Вот мой полный запуск этого (в Rails 5):
У меня есть team_id как индекс в производителях таблиц. Мне больше не нужно это отношение. Чтобы избавиться от него. Сделал следующее:
1) создать миграцию.
$ rails generate migration RemoveTeam_idFromVendor team_id:integer
2) Запустив миграцию, дайте мне эту ошибку. И это потому, что в таблице поставщиков есть строки, чей внешний ключ ссылается на значение первичного ключа таблицы команд.
== 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) Чтобы решить эту проблему и выполнить миграцию, я сделал следующее (Примечание: я в dev):
$ 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) ==================