2015-11-07 42 views
2

我有一個STI模型,其中每個子類使用不同的驗證。 Rails默認會在運行時運行原始類型的驗證,所以我試圖用「變成」來強制新類型的驗證。單表繼承和更改類型不會保存

我的代碼如下所示:

payment_processor = PaymentProcessor.where(:account_id => self.id).first_or_initialize 

new_gateway = "PaymentProcessors::#{gateway_type.classify}".constantize 
payment_processor = payment_processor.becomes(new_gateway) 
payment_processor.type = new_gateway 

payment_processor.assign_attributes(attributes) 
payment_processor.save! 

但是,它不會保存,因爲MySQL的生成過程中保存正在尋找新的類型。因此,舉例來說,如果我最初gateway_type是「AuthorizeNet」,我更改爲「貝寶」,MySQL的是:

UPDATE `payment_processors` SET `type` = 'PaymentProcessors::PayPal', `updated_at` = '2015-11-07 11:53:53' WHERE `payment_processors`.`type` IN ('PaymentProcessors::PayPal') AND `payment_processors`.`id` = 232 

但要尋找原始類型,Auth.net是這樣的:

UPDATE `payment_processors` SET `type` = 'PaymentProcessors::PayPal', `updated_at` = '2015-11-07 11:53:53' WHERE `payment_processors`.`type` IN ('PaymentProcessors::AuthorizeNet') AND `payment_processors`.`id` = 232 

有關如何跳過「where子句」以僅由payment_processor ID更新的任何想法?

回答

0

我知道STI在嘗試類型更改時驗證失敗。 (閱讀更多http://blog.arkency.com/2013/07/sti)。所以,我通過總是創建一個新的payment_processor來解決問題,但如果驗證失敗,則恢復舊的問題。這是不雅,但它的作品。

然後複製原來的payment_processor刪除:

original = self.payment_processor.destroy 
self.payment_processor.destroy 

不是傳遞新類型的,我們從PARAMS刪除它,但使用new_gateway_type創建新的網關。

new_gateway_type = params[:payment_processor][:gateway_type] 
params[:payment_processor].delete(:gateway_type) 

例如,如果新的網關是「寶」,我們將constantize的new_gateway_type創建一個新的對象,然後更新PARAMS:

payment_processor = "PaymentProcessors::#{new_gateway_type}".constantize.new(:account_id => self.id) 
payment_processor.update_attributes(params[:payment_processor) 

最後,保存新的網關。如果此new_gateway的驗證失敗,我們通過對對象模型名稱進行常量化並傳入除屬性和ID之外的新屬性來還原舊的:

begin 
    payment_processor.save! 
rescue ActiveRecord::RecordInvalid => invalid 
    original.type.constantize.new(original.attributes.except("id", "type")).save 
end