Rails 5 - 在 rake 任务中生成和 运行 迁移
Rails 5 - generate and run migrations inside a rake task
所以我有一个应用程序,当我创建一个新用户时,我会在与该特定用户相关的其他一些数据库中设置列。我意识到这不是最佳实践,但对于我的用例来说,它比序列化一个包含 table.
所有用户信息的数组要快得多
我想做的是设置一个创建 User
的 rake 任务,以及对 table 进行必要的迁移。
这是我目前的情况:
desc "Adds User and creates correct DB entries."
task add_user: :environment do
username = ENV['username'].to_s
email = ENV['email'].to_s
password = ENV['password'].to_s
initials = ENV['initials'].to_s
if username and email and password and initials
User.create! :username => username, :email => email, :password => password, :password_confirmation => password, :initials => initials
Rake::Task['generate migration AddPay' + initials + 'ToShoppingLists pay' + initials + ':decimal'].invoke
Rake::Task['generate migration AddPay' + initials + 'ToPayments pay' + initials + ':decimal'].invoke
Rake::Task['db:migrate'].invoke
end
end
我的问题是在 Rails 5
中,我必须 运行 rails g migration
而不是 rake g migration
所以我不确定如何调用 rails
命令在 rake
任务中。
另外,有没有办法检查迁移是否已经创建?例如,如果我在开发模式下运行,我不需要在生产模式下重新创建迁移,只需执行db:migrate
。
您可以使用 Rake
的 sh
方法,只需调用 rails
shell 命令即可。
sh "rails g migration AddPay#{initials}ToShoppingLists pay#{initials}:decimal"
sh "rails g migration AddPay#{initials}ToPayments pay#{initials}:decimal"
当使用 sh
而不是 ruby 的内置反引号定界符用于 shell 命令时,如果命令的退出状态不是 0
它将引发异常并中止任务。
要查看您的迁移是否已创建,您只需检查是否存在与命名模式匹配的迁移文件。
files = Dir.glob Rails.root.join('db/migrate/*')
migration_patterns = {
/add_pay_#{initials.downcase}_to_shopping_lists/ => "rails g migration AddPay#{initials}ToShoppingLists pay#{initials}:decimal",
/add_pay_#{initials.downcase}_to_payments/ => "rails g migration AddPay#{initials}ToPayments pay#{initials}:decimal"
}
migration_patterns.each do |file_pattern, migration_command|
if files.none? { |file| file.match? file_pattern }
sh migration_command
end
end
Rake::Task['db:migrate'].invoke
这假设您在 none?
中不会有任何引起误报的迁移命名冲突。但是 Rails 不会让你有迁移命名冲突,所以检查可能没有必要。鉴于您命名迁移和列的方式,您最终似乎注定要 运行 解决这个问题。如果两个用户的首字母相同怎么办?
可能有一种方法可以通过使用额外的数据库 table(也许是多态连接 table?)而不是为每个 user
添加列来完成您需要的工作?这些方面的东西可能会起作用:
class CreateDisbursements < ActiveRecord::Migration[5.1]
def change
create_table :disbursements do |t|
t.decimal :amount
t.integer :payable_id
t.string :payable_type
t.integer :receivable_id
t.string :receivable_type
t.timestamps
end
add_index :disbursements, [:payable_type, :payable_id]
add_index :disbursements, [:receivable_id, :receivable_type]
end
end
class Disbursement < ApplicationRecord
belongs_to :payable, polymorphic: true
belongs_to :receivable, polymorphic: true
end
class ShoppingList < ApplicationRecord
has_many :disbursements, as: :payable
has_many :users, through: :disbursements, source: :receivable, source_type: 'User'
end
class Payment < ApplicationRecord
has_many :disbursements, as: :payable
has_many :users, through: :disbursements, source: :receivable, source_type: 'User'
end
class User < ApplicationRecord
has_many :disbursements, as: :receivable
has_many :payments, through: :disbursements, source: :payable, source_type: 'Payment'
has_many :shopping_lists, through: :disbursements, source: :payable, source_type: 'ShoppingList'
end
user = User.find params[:user_id]
payment = Payment.find params[:payment_id]
amount = params[:amount]
payment.disbursements.create(amount: amount, receivable: user)
user.disbursements.create(amount: amount, payable: payment)
Disbursement.create(amount: amount, payable: payment, receivable: user)
user.payments
payment.users
所以我有一个应用程序,当我创建一个新用户时,我会在与该特定用户相关的其他一些数据库中设置列。我意识到这不是最佳实践,但对于我的用例来说,它比序列化一个包含 table.
所有用户信息的数组要快得多我想做的是设置一个创建 User
的 rake 任务,以及对 table 进行必要的迁移。
这是我目前的情况:
desc "Adds User and creates correct DB entries."
task add_user: :environment do
username = ENV['username'].to_s
email = ENV['email'].to_s
password = ENV['password'].to_s
initials = ENV['initials'].to_s
if username and email and password and initials
User.create! :username => username, :email => email, :password => password, :password_confirmation => password, :initials => initials
Rake::Task['generate migration AddPay' + initials + 'ToShoppingLists pay' + initials + ':decimal'].invoke
Rake::Task['generate migration AddPay' + initials + 'ToPayments pay' + initials + ':decimal'].invoke
Rake::Task['db:migrate'].invoke
end
end
我的问题是在 Rails 5
中,我必须 运行 rails g migration
而不是 rake g migration
所以我不确定如何调用 rails
命令在 rake
任务中。
另外,有没有办法检查迁移是否已经创建?例如,如果我在开发模式下运行,我不需要在生产模式下重新创建迁移,只需执行db:migrate
。
您可以使用 Rake
的 sh
方法,只需调用 rails
shell 命令即可。
sh "rails g migration AddPay#{initials}ToShoppingLists pay#{initials}:decimal"
sh "rails g migration AddPay#{initials}ToPayments pay#{initials}:decimal"
当使用 sh
而不是 ruby 的内置反引号定界符用于 shell 命令时,如果命令的退出状态不是 0
它将引发异常并中止任务。
要查看您的迁移是否已创建,您只需检查是否存在与命名模式匹配的迁移文件。
files = Dir.glob Rails.root.join('db/migrate/*')
migration_patterns = {
/add_pay_#{initials.downcase}_to_shopping_lists/ => "rails g migration AddPay#{initials}ToShoppingLists pay#{initials}:decimal",
/add_pay_#{initials.downcase}_to_payments/ => "rails g migration AddPay#{initials}ToPayments pay#{initials}:decimal"
}
migration_patterns.each do |file_pattern, migration_command|
if files.none? { |file| file.match? file_pattern }
sh migration_command
end
end
Rake::Task['db:migrate'].invoke
这假设您在 none?
中不会有任何引起误报的迁移命名冲突。但是 Rails 不会让你有迁移命名冲突,所以检查可能没有必要。鉴于您命名迁移和列的方式,您最终似乎注定要 运行 解决这个问题。如果两个用户的首字母相同怎么办?
可能有一种方法可以通过使用额外的数据库 table(也许是多态连接 table?)而不是为每个 user
添加列来完成您需要的工作?这些方面的东西可能会起作用:
class CreateDisbursements < ActiveRecord::Migration[5.1]
def change
create_table :disbursements do |t|
t.decimal :amount
t.integer :payable_id
t.string :payable_type
t.integer :receivable_id
t.string :receivable_type
t.timestamps
end
add_index :disbursements, [:payable_type, :payable_id]
add_index :disbursements, [:receivable_id, :receivable_type]
end
end
class Disbursement < ApplicationRecord
belongs_to :payable, polymorphic: true
belongs_to :receivable, polymorphic: true
end
class ShoppingList < ApplicationRecord
has_many :disbursements, as: :payable
has_many :users, through: :disbursements, source: :receivable, source_type: 'User'
end
class Payment < ApplicationRecord
has_many :disbursements, as: :payable
has_many :users, through: :disbursements, source: :receivable, source_type: 'User'
end
class User < ApplicationRecord
has_many :disbursements, as: :receivable
has_many :payments, through: :disbursements, source: :payable, source_type: 'Payment'
has_many :shopping_lists, through: :disbursements, source: :payable, source_type: 'ShoppingList'
end
user = User.find params[:user_id]
payment = Payment.find params[:payment_id]
amount = params[:amount]
payment.disbursements.create(amount: amount, receivable: user)
user.disbursements.create(amount: amount, payable: payment)
Disbursement.create(amount: amount, payable: payment, receivable: user)
user.payments
payment.users