Как правильно обрабатывать перенос данных в Ruby на Rails? - PullRequest
1 голос
/ 07 февраля 2020

У меня проблема с тем, что наши файлы миграции (которые выполняют изменения данных) зависят от базы данных, которая еще не была перенесена.

Мы используем Rails 6, который поддерживает несколько баз данных:

production:
  primary:
    database: my_db_name
  other:
    database: other_db_name

Вот пример наших моделей:

class Product < ApplicationRecord
  belongs_to :other
end

class Other < ApplicationRecord
  establish_connection: :other
end

Проблема существует, когда миграция для основной базы данных пытается использовать данные из другой базы данных.

class AddInitialProducts < ActiveRecord::Migration[6.0]  
  def up
    obj = Other.find_by(name: "Special Object")
    Product.create(name: "Product", other: obj)
  end

  def down
    # Delete product created above.
  end
end

Когда я запускаю rails db:migrate при запуске с пустой базой данных, он пытается сначала перенастроить базу данных primary, что не удается, поскольку other_db_name еще не существует. Он не был перенесен.

Мне известна команда rails db:schema:load, но нам нужны данные, чтобы существовать в нашем приложении. Это также тривиальный пример данных, которые необходимы. Вполне возможно, что между каждым выпуском приложения будут происходить миграции данных, поэтому seed не кажется хорошей идеей, поскольку этот файл предназначен для разработки / тестирования.

Как правильно обращаться миграция данных между выпусками?

1 Ответ

0 голосов
/ 08 февраля 2020

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

Вместо этого вам нужно выполнить задачу rake, возможно, в сочетании с объектом службы. Вы можете генерировать рейковые задачи с помощью:

rails g task products import

Генератор сгенерирует:

# lib\tasks\products.rake
namespace :products do
  desc "TODO"
  task import: :environment do
  end
end

Эта задача может быть вызвана с помощью bin/rake products:import в любой выбранной вами точке.

Конечно, не будет никакого вывода, так как задача еще ничего не делает. Фактическая реализация идет в блоке, переданном в task. Тестирование Rake-задач может быть довольно сложным, поэтому я считаю, что лучше всего выполнять большинство реальных реализаций в сервисных объектах:

class ProductImporter
  def initialize(env, **kwargs)
    # set up
  end

  def call
    # do actual work 
    # ...
    teardown
  end

  def self.call(env, **kwargs)
    new(kwargs).call
  end

  private 

  def teardown
    # clean up
  end
end

, которые легко протестировать, поскольку это просто старые ruby объекты и вы не беспокойтесь о таких вещах, как аргументы командной строки.

А затем просто вызывайте ваши службы из задачи:

# lib\tasks\products.rake
namespace :products do
  desc "Import a list of products from..."
  task import: :environment do
    ProductImporter.call(Rails.env)
  end
end

Это делает так, что вам просто нужно проверить, что ваши грабли задача вызывает службу с правильными аргументами.

...