Обратимые миграции в 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.

Оригинал статьи на английском:

 

Лучшая благодарность автору блога — ваши комментарии! Наши ошибку или неточность? — Пожалуйста, сообщите в комментариях.

Tags: , ,

Responses

  1. Sergey says:

    мая 10, 2011 at 20:50 (#)

    Чувак! Огромное спасибо за статьи, который ты пишешь или переводишь. Я в недалеком будущем хочу переползти с PHP на ROR и ты мне в этом сильно помогаешь.

  2. admin says:

    мая 10, 2011 at 21:12 (#)

    Спасибо за спасибо! Если есть какие-то пожелания по статьям — отписывайтесь, не гарантирую, что напишу, но постараюсь.

  3. GarPit says:

    мая 11, 2011 at 08:14 (#)

    «Если Rails сталкивается как команды пока откатывается миграция…» — переводи плиз сразу на русский, а не на таджикско-русский =) А так классная статья, спасибо!

  4. Maxim Filatov says:

    мая 11, 2011 at 08:27 (#)

    Не Хосе Вэлим, а Жозе Валим. По крайней мере сам он представляется именно так. И еще есть ошибки в именах методов.

  5. says:

    мая 11, 2011 at 14:37 (#)

    Ух… не «Хосе Вэлим», а Жозе ВалИм.

  6. admin says:

    мая 11, 2011 at 15:51 (#)

    Спасибо, проправил все. Почему-то думал, что Хосе — правильнее, так как Жозе никода не слышал.

  7. ryuk says:

    мая 11, 2011 at 16:27 (#)

    «»"
    как например remove_colimn
    «»"
    Явно remove_column

  8. says:

    мая 14, 2011 at 07:28 (#)

    oftop
    «…что Хосе – правильнее…» это потому, что нас пичкали мексиканскими сериалами и учили испанскому ;-), а Валим — бразилец и на португальском его имя звучит примерно так

    p.s. рекомендую откопать и послушать в оригинале фильм который у нас шел как «генералы песчаных карьеров», «Besoro» или «DONA FLOR E SEUS DOIS MARIDOS» там можна услышать очень красивый португальский

  9. Ketzer says:

    октября 17, 2011 at 22:31 (#)

    Спасибо. очень в тему как раз с миграциями разбираюсь, сначала нашёл оригинал статьи, не осилил (( спасибо за перевод )

  10. admin says:

    октября 17, 2011 at 23:05 (#)

    Ketzer, всегда пожалуйста и Welcome to , там я и другие участники постараемся помочь в решении любых проблемы и ответить на любые вопросы.

Leave a Response

Для подсветки кода используйте BB - коды: [language]...[/language], где language может быть: ruby, javascript, css, html.