2015-11-07 44 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