Cofnięcie nieudanej migracji Rails

Jak cofnąć nieudaną migrację rails? Spodziewałbym się, że rake db:rollback cofnie nieudaną migrację, ale nie, cofnie poprzednią migrację (nieudana migracja minus jeden). I rake db:migrate:down VERSION=myfailedmigration też nie działa. Wpadłem na to kilka razy i to jest bardzo frustrujące. Oto prosty test, który zrobiłem, aby zduplikować problem:

class SimpleTest < ActiveRecord::Migration
  def self.up
    add_column :assets, :test, :integer
    # the following syntax error will cause the migration to fail
    add_column :asset, :test2, :integer
  end

  def self.down
    remove_column :assets, :test
    remove_column :assets, :test2
  end
end

Wynik:

==  SimpleTest: migrating =====================================================
-- add_column(:assets, :test, :integer)
   -> 0.0932s
-- add_column(:asset, :error)
rake aborted!
An error has occurred, all later migrations canceled:

wrong number of arguments (2 for 3)

Ok, cofnijmy to:

$ rake db:rollback
==  AddLevelsToRoles: reverting ===============================================
-- remove_column(:roles, :level)
   -> 0.0778s
==  AddLevelsToRoles: reverted (0.0779s) ======================================
Co? to była moja ostatnia migracja przed SimpleTest, nie nieudana migracja. (I byłoby miło, gdyby wyjście migracji zawierało numer wersji.)

Więc spróbujmy uruchomić down dla nieudanej migracji SimpleTest:

$ rake db:migrate:down VERSION=20090326173033
$

Nic się nie dzieje i nie ma wyjścia. Ale może i tak przeprowadził migrację? Więc pozwala naprawić błąd składni w najprostszej migracji, i spróbować uruchomić go ponownie.

$ rake db:migrate:up VERSION=20090326173033
==  SimpleTest: migrating =====================================================
-- add_column(:assets, :test, :integer)
rake aborted!
Mysql::Error: Duplicate column name 'test': ALTER TABLE `assets` ADD `test` int(11)
Nie. Oczywiście migrate: down nie działa. Nie zawodzi, po prostu nie wykonuje.

Nie sposób pozbyć się tej zduplikowanej tabeli poza ręcznym wejściem do bazy danych i usunięciem jej, a następnie uruchomieniem testu. Musi być lepszy sposób.

Author: CDspace, 2009-03-26

9 answers

Niestety, musisz ręcznie wyczyścić nieudane migracje dla MySQL. MySQL nie obsługuje zmian definicji transakcyjnych baz danych.

Rails 2.2 zawiera migracje transakcyjne dla PostgreSQL. Rails 2.3 zawiera migracje transakcyjne dla SQLite.

To naprawdę nie pomaga na twój problem w tej chwili, ale jeśli masz wybór bazy danych o przyszłych projektach, polecam użycie jednej z obsługą transakcji DDL, ponieważ sprawia, że migracje znacznie więcej miło.

Aktualizacja - to nadal prawda w 2017, on Rails 4.2.7 i MySQL 5.7, zgłoszone przez Alejandro Babio w innej odpowiedzi tutaj.

 75
Author: Luke Francl,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-05 19:10:33

Aby przejść do określonej wersji wystarczy użyć:

rake db:migrate VERSION=(the version you want to go to)

Ale jeśli migracja zawiedzie częściowo, musisz ją najpierw wyczyścić. Jednym ze sposobów byłoby:

  • edytuj metodę migracji down, aby cofnąć część up, która zadziałała
  • migracja z powrotem do poprzedniego stanu (od którego zacząłeś)
  • napraw migrację (w tym cofanie zmian do down)
  • spróbuj ponownie
 20
Author: MarkusQ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-03-26 18:00:38

Ok, ludzie, oto jak to się robi. Nie wiem, o czym mówią powyższe odpowiedzi.

  1. Dowiedz się, która część migracji up zadziałała. Skomentuj to.
  2. również skomentuj / usuń część migracji, która się zepsuła.
  3. Uruchom migrację ponownie. Teraz zakończy niezłamane części migracji, pomijając części, które już zostały zrobione.
  4. odkomentuj fragmenty migracji, które skomentowałeś w kroku 1.

Możesz przenieść w dół i utworzyć kopię zapasową, jeśli chcesz sprawdzić, czy masz ją teraz.

 18
Author: Simon Woodside,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-03-29 01:01:15

Zgadzam się, że powinieneś używać PostgreSQL, jeśli to możliwe. Jednak, gdy utkniesz z MySQL, możesz uniknąć większości tych problemów, próbując najpierw migracji na testowej bazie danych:

rake db:migrate RAILS_ENV=test

Możesz powrócić do poprzedniego stanu i spróbować ponownie za pomocą

rake db:schema:load RAILS_ENV=test
 12
Author: StefanH,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-12-10 10:09:53

W 2015 roku z Rails 4.2.1 i MySQL 5.7, nieudana migracja nie może być naprawiona za pomocą standardowych akcji rake, które rails zapewniają, Jak to było w 2009 roku.

MySql nie obsługuje wycofywania Stanów DDL (w MySQL 5.7 Manual ). A Rails nie może nic z tym zrobić.

Możemy również sprawdzić, jak Rails wykonuje zadanie: migracja jest zawinięta w transakcję w zależności od tego, jak adapter reaguje na :supports_ddl_transactions?. Po przeszukaniu tej akcji w Rails source (v 4.2.1), odkryłem, że tylko Sqlite3 i PostgreSql obsługuje transakcje, a przez domyślnie nie jest obsługiwany.

Edit Tak więc aktualna odpowiedź na pierwotne pytanie: nieudana migracja MySQL musi być ręcznie naprawiona.

 9
Author: Alejandro Babio,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-05-03 15:12:21

Łatwy sposób na to, aby to zrobić, to zawinąć wszystkie swoje działania w transakcję:

class WhateverMigration < ActiveRecord::Migration

 def self.up
    ActiveRecord::Base.transaction do
...
    end
  end

  def self.down
    ActiveRecord::Base.transaction do
...
    end
  end

end

Jak zauważył Luke Francl, " MySql ['s MyISAM tables don' t] support transactions " - dlatego możesz rozważyć unikanie MySQL w ogóle lub przynajmniej MyISAM w szczególności.

Jeśli używasz InnoDB MySQL, to powyższe będzie działać dobrze. Wszelkie błędy w górę lub w dół wycofają się.

Należy pamiętać niektóre rodzaje akcji nie mogą być cofnięte poprzez transakcje. Ogólnie, zmiany tabeli (upuszczanie tabeli, usuwanie lub dodawanie kolumn itp.)nie można cofnąć.

 8
Author: BryanH,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-04-01 17:41:30

Uruchom tylko migrację down z konsoli:

Http://gilesbowkett.blogspot.com/2007/07/how-to-use-migrations-from-console.html (click through to his pastie)

 1
Author: Ivan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-01-24 22:37:05

Miałem literówkę (w "add_column"):

Def self.up

add_column :medias, :title, :text
add_colunm :medias, :enctype, :text

End

Def self.down

remove_column :medias, :title
remove_column :medias, :enctype   

End

A następnie twój problem(nie można cofnąć częściowo nieudanej migracji). po jakimś nieudanym googlowaniu uruchomiłem to:

Def self.up

remove_column :medias, :title
add_column :medias, :title, :text
add_column :medias, :enctype, :text

End

Def self.down

remove_column :medias, :title
remove_column :medias, :enctype

End

Jak widzisz, dodałem linię korekcji ręcznie, a następnie usunąłem ją ponownie, zanim sprawdziłem.

 1
Author: poster,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-03-02 10:04:42

Powyższa odpowiedź Alejandro Babio zapewnia najlepszą aktualną odpowiedź.

Jeden dodatkowy szczegół chcę dodać:

Gdy migracja myfailedmigration nie powiedzie się, nie jest uważana za zastosowaną i można to sprawdzić uruchamiając rake db:migrate:status, które wyświetli wynik podobny do następującego:

$  rake db:migrate:status
database: sample_app_dev

 Status   Migration ID    Migration Name
--------------------------------------------------
   up      20130206203115  Create users
   ...
   ...
   down    20150501173156  Test migration

Pozostały efekt add_column :assets, :test, :integer po nieudanej migracji będzie musiał zostać odwrócony na poziomie bazy danych za pomocą zapytania alter table assets drop column test;.

 1
Author: Prakash Murthy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-05-01 18:07:02