2017-07-28 95 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的底部添加了一些示例來處理和構建這些關聯。根據你需要做什麼,這個設置可能意味着更多的數據庫查詢,但我認爲它使得數據和關係靈活且非常容易處理 –