Rails: как создать миграцию в подкаталоге с помощью Rails?

Я пишу приложение модели SaaS. База данных моего приложения состоит из двух логических частей:

  • таблицы приложений - такие как пользователь, роли...
  • определяемые пользователем таблицы (он может генерировать их на уровне пользовательского интерфейса), которые могут быть разными для каждого экземпляра приложения

Все таблицы создаются с помощью механизма миграции rails.

Я хотел бы поместить пользовательские таблицы в другой каталог:

  • db/migrations — таблицы приложений
  • db/migrations/custom — таблицы, сгенерированные пользователем

поэтому я могу выполнить svn:ignore для db/migrations/custom, и когда я обновляю свое приложение на клиентских серверах, оно будет обновлять только миграции таблиц приложений.

Есть ли способ добиться этого в рельсах?


person Adrian Serafin    schedule 08.01.2011    source источник
comment
Я использую github.com/thuss/standalone-migrations для решения этой задачи.   -  person eebbesen    schedule 21.05.2015


Ответы (5)


Задача rake db:migrate имеет жестко запрограммированный путь к миграции. Но вы можете создать свою собственную рейк-задачу. Например, создайте lib/tasks/custom_db_migrate.rake со следующим содержимым:

namespace :db do
  task :custom_migrate => :environment do
    ActiveRecord::Migration.verbose = ENV["VERBOSE"] ? ENV["VERBOSE"] == "true" : true
    ActiveRecord::Migrator.migrate("db/migrate/custom", ENV["VERSION"] ? ENV["VERSION"].to_i : nil)
    Rake::Task["db:schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby
  end
end

Теперь вы можете запустить rake db:custom_migrate для запуска миграций, расположенных в db/migrate/custom. Но он не будет использовать миграции из пути по умолчанию.

Вы можете прочитать исходный код для миграции ActiveRecord.

person Vasily Reys    schedule 08.01.2011

Если вы используете Sinatra и создаете собственную задачу rake, вы можете сделать следующее:

require './app'
require 'sinatra/activerecord/rake'

ActiveRecord::Migrator.migrations_paths = 'your/path/goes/here'

Когда вы запустите rake -T, вы получите пространство имен db:

rake db:create_migration  # create an ActiveRecord migration
rake db:migrate           # migrate the database (use version with VERSION=n)
rake db:rollback          # roll back the migration (use steps with STEP=n)
person John Hamelink    schedule 04.04.2013

@Василий спасибо за ответ. Прочитав его и еще пару вопросов из stackoverflow, я пришел к следующему решению:

Поскольку я пишу свой собственный генератор для создания пользовательских таблиц, я включил в него Rails::Generators::Migration, чтобы я мог переопределить метод next_migration_number следующим образом:

def self.next_migration_number(dirname)
 if ActiveRecord::Base.timestamped_migrations
   Time.now.utc.strftime("custom/%Y%m%d%H%M%S")
 else
   "custom/%.3d" % (current_migration_number(dirname) + 1)
 end
end

Теперь все миграции, созданные пользователем, создаются в каталоге db/migrations/custom.

Затем я написал обычную миграцию rails, которая выполняет все миграции из каталога db/migrations/custom:

class ExecuteCustomMigrations < ActiveRecord::Migration
   MIGRATIONS_PATH='db/migrate/custom'
   def self.up
     Dir["#{MIGRATIONS_PATH}/[0-9]*_*.rb"].
     sort.map{|filename|require filename}.flatten.
     each{|class_name| const_get(class_name).up}
   end

   def self.down
     Dir["#{MIGRATIONS_PATH}/[0-9]*_*.rb"].sort.reverse.
     map{|filename|require filename}.flatten.
     each{|class_name| const_get(class_name).down}
   end
end

После того, как пользователь создает пользовательскую таблицу, я вызываю эту миграцию с помощью этого кода:

Rake::Task["db:migrate:redo"].execute("VERSION=20110108213453")
person Adrian Serafin    schedule 09.01.2011

Обновление для Rails 5/6;

Rails 5 рекомендует указать дополнительные пути миграции в файле config/database.yml. Это очень просто, см. этот пример;

development:
  migrations_paths:
  - db/migrations
  - db/migrations/custom

ActiveRecord::Migrator.migrations_path= будет объявлен устаревшим в Rails 6.

person theallseeingguy    schedule 29.01.2019

С rails 4 мы видим, что каталоги миграции хранятся в массиве, доступ к которому осуществляется с помощью «db/migrate».

Фрагмент кода из activerecord/lib/active_record/migration.rb

def migrations_paths
    @migrations_paths ||= ["db/migrate"]
    # just to not break things if someone uses: migrations_path = some_string
    Array(@migrations_paths) # Data stored in an array
end

Так что мы можем добавить к этому массиву конфиг в environment.rb, например

Rails.application.configure do
    config.paths["db/migrate"] << %Q{db/migrations}
    config.paths["db/migrate"] << %Q{db/migrations.custom}
end

Кроме того, я не смог найти это задокументировано, но дополнительные каталоги в db/migrate также ищутся и выполняются.

например Я помещаю группы миграций в каталоги релизов

-db/migrate
    -3.0.0
       XXXXXcreate_user.rb
    -3.0.1
       XXXXXcreate_task.rb

Этот механизм также используется для добавления каталогов миграции движка обсуждается здесь

person Dave Benson    schedule 02.01.2018