2017-07-28 53 views
0

所以我有一个应用程序,当我创建一个新用户时,我在与该特定用户相关的其他数据库中设置了列。我意识到这不是最佳实践,但对于我的使用情况来说,它比序列化保存该表所有用户信息的数组快得多。Rails 5 - 在rake任务中生成并运行迁移

我想要做的是设置一个rake任务,该任务创建User,并执行必要的表迁移。

这是我到目前为止有:

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 migrationrake g migration所以我不能确定如何从rake任务内调用rails命令。

此外,有没有办法检查是否已经创建了迁移?例如,如果我以开发模式运行此操作,则不需要在生产模式下重新创建迁移,只需执行db:migrate即可。

回答

1

您可以使用Rakesh方法,只需调用rails shell命令。

sh "rails g migration AddPay#{initials}ToShoppingLists pay#{initials}:decimal" 
sh "rails g migration AddPay#{initials}ToPayments pay#{initials}:decimal" 

当使用sh而不是红宝石的内置反引号分隔符的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不会让你迁移命名冲突,所以检查可能没有必要。看起来你肯定会遇到这个问题,最终会考虑到你命名迁移和列的方式。如果两个用户具有相同的首字母缩写呢?

可能有一种方法可以通过使用额外的数据库表(可能是多态连接表?)而不是为每个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 
+0

谢谢,这是完美的 - 用户将不会有初始冲突,因为它被设置为唯一。 (他们不是真正的名字更像id键)。我会对你的第二个建议有所了解,看看我是否保持了我所需要的速度。 – Darkstarone

+1

很高兴帮助!我在一些API的底部添加了一些示例来处理和构建这些关联。根据你需要做什么,这个设置可能意味着更多的数据库查询,但我认为它使得数据和关系灵活且非常容易处理 –