Обратимые миграции в Rails 3.1
мая 10, 2011 | Published in Ruby on Rails, Ruby on Rails 3, Базы данных | 10 Comments
Миграции всегда были одной из основных «фишек» Ruby on Rails. В Rails 3.1 миграции приобретают еще один козырь, который упрощает процесс поддержки логики в up и down методах миграции. Если вам нужно немного восстановить свои знания о миграциях, я рекомендую прочитать официальный .
Давайте начнем с разбора примера миграции из Rails 3.0:
#db/migrate/20110505090317_create_posts.rb class CreatePosts < ActiveRecord::Migration def self.up create_table :posts do |t| t.string :title t.text :body t.timestamps end end def self.down drop_table :posts end end
Эта миграция создает в базе данных таблицу posts с двумя полями — title и body тип хранимых данных в которых соответственно string и text. Хэлпер timestamps создает поля типа datetime — created_at и updated_at. Для отката этой миграции нам просто необходимо удалить таблицу posts. Метод .down как раз и занимается откатом миграции — удалением таблицы posts. Когда Rails использует миграцию, миграция запускает метод .up. Для отката миграции (которая выполняется командой rake db:rollback) миграция выполняет метод .down.
Глядя на эту миграцию возникают два вопроса:
- Почему именно методы класса, вместо методов экземпляра класса?
- Для простых случаев почему бы не объявить только метод выполнения миграции и защитить миграцию от отката?
Замечательный человек Аарон Петерсон думает так же и решил упросить миграции.
Знакомьтесь, упрощенные миграции!
Если вы запустите следущую команду в Edge Rails, то сгенерированная миграция будет выглядеть весьма похоже на пример выше:
#console
$ rails g model Post title:string body:text
#db/migrate/20110505084530_create_posts.rb class CreatePosts < ActiveRecord::Migration def change create_table :posts do |t| t.string :title t.text :body t.timestamps end end end
По умолчанию Rails 3.1 будет генерировать миграции для моделей использую метод #change, который теперь будет хранить логику ранее хранимую в методе .up. Когда запускается rollback, Rails будет выполнять откат миграции просто оценивая инструкции в «up». Следуем дальше, выполняем миграции и затем делаем откат. Выполняя все это, вы должны будете увидеть в консоли что-то вроде этого:
$ rake db:migrate
(in /home/rohit/code/migrahedron)
== CreatePosts: migrating ====================================================
— create_table(:posts)
-> 0.0012s
== CreatePosts: migrated (0.0013s) ===========================================
$ rake db:rollback
(in /home/rohit/code/migrahedron)
== CreatePosts: reverting ====================================================
— drop_table(«posts»)
-> 0.0007s
== CreatePosts: reverted (0.0008s) ===========================================
Обратите внимание на то как Rails показывает что в случае отката миграции, она нуждается в удалении созданной таблицы.
Что на счет команд, которые не могут быть отменены?
Существуют определенные команды, как например remove_column, которые не могут быть автоматически отменены. Это так потому, что информация необходимая для повторного создания столбца не доступна в команде remove_column. Если Rails во время отката миграции столкнется с командой, которую откатить нельзя, то будет возвращена ошибка: ActiveRecord::IrreversibleMigration.
#db/migrate/20110505101449_remove_title_from_post.rb class RemoveTitleFromPost < ActiveRecord::Migration def change remove_column :posts, :title end end
Если вы попытаетесь сделать откат вышеприведенной миграции, то вы получите что-то вроде этого:
#console
$ rake db:rollback
(in /home/rohit/code/migrahedron)
== RemoveTitleFromPost: reverting ============================================
rake aborted!
An error has occurred, this and all later migrations canceled:
ActiveRecord::IrreversibleMigration
(See full trace by running task with —trace)
Если вы хотите обработать подобные случаи вручную вы можете по прежнему объявлять методы #up и #down, почти как раньше.
up и down теперь методы экземпляра класса
Все, что нужно для приведения старых миграций в новый вид — это объявить методы #up и #down как методы экземпляра класса, просто удалите приемник self перед именами методов #up и #down:
#db/migrate/20110505101557_remove_title_from_post.rb class RemoveTitleFromPost < ActiveRecord::Migration def up remove_column :posts, :title end def down add_column :posts, :title, :string end end
Если вам сложно внести изменения,то вы можете продолжить использовать старый стиль написания миграция с методами класса. Очень важно, чтобы ваши существующие миграции были работоспособны и в новых версиях фреймворка.
Больше магии?
Если вы удивлены тем как определяются откаты миграций и в духе Жозе Валима желаете увидить всю магию Rails, я думаю, я предоставлю вам простое описание того, как Rails автоматически делает откат миграций.
Магия происходит в классе . Если вы объявляете метод #change в вашей миграции и запускаете миграцию, то код #change выполняется аналогично коду из #up.
Однако когда происходит откат миграции, запускается сгенерированный список реверсивных команд. Команды отката — это просто команды, которые выполняют противоположные действия по отношению к действиям команд миграций. Например, обратная команда для rename_table(old, new) это rename_table(new, old). Логика получения обратной команды предоставляется самим классом. Для тех команд, инверсии которых не могут быть созданы, вызывается исключительная ситуация ActiveRecord::IrreversibleMigration.
Оригинал статьи на английском:
Лучшая благодарность автору блога — ваши комментарии! Наши ошибку или неточность? — Пожалуйста, сообщите в комментариях.
мая 10, 2011 at 20:50 (#)
Чувак! Огромное спасибо за статьи, который ты пишешь или переводишь. Я в недалеком будущем хочу переползти с PHP на ROR и ты мне в этом сильно помогаешь.
мая 10, 2011 at 21:12 (#)
Спасибо за спасибо! Если есть какие-то пожелания по статьям — отписывайтесь, не гарантирую, что напишу, но постараюсь.
мая 11, 2011 at 08:14 (#)
«Если Rails сталкивается как команды пока откатывается миграция…» — переводи плиз сразу на русский, а не на таджикско-русский =) А так классная статья, спасибо!
мая 11, 2011 at 08:27 (#)
Не Хосе Вэлим, а Жозе Валим. По крайней мере сам он представляется именно так. И еще есть ошибки в именах методов.
мая 11, 2011 at 14:37 (#)
Ух… не «Хосе Вэлим», а Жозе ВалИм.
мая 11, 2011 at 15:51 (#)
Спасибо, проправил все. Почему-то думал, что Хосе — правильнее, так как Жозе никода не слышал.
мая 11, 2011 at 16:27 (#)
«»"
как например remove_colimn
«»"
Явно remove_column
мая 14, 2011 at 07:28 (#)
oftop
«…что Хосе – правильнее…» это потому, что нас пичкали мексиканскими сериалами и учили испанскому ;-), а Валим — бразилец и на португальском его имя звучит примерно так
p.s. рекомендую откопать и послушать в оригинале фильм который у нас шел как «генералы песчаных карьеров», «Besoro» или «DONA FLOR E SEUS DOIS MARIDOS» там можна услышать очень красивый португальский
октября 17, 2011 at 22:31 (#)
Спасибо. очень в тему как раз с миграциями разбираюсь, сначала нашёл оригинал статьи, не осилил (( спасибо за перевод )
октября 17, 2011 at 23:05 (#)
Ketzer, всегда пожалуйста и Welcome to , там я и другие участники постараемся помочь в решении любых проблемы и ответить на любые вопросы.