我有一个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"而且我正在改变为#Pay; PayPal",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 :(得分: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)
例如,如果新网关是Paypal',我们将对new_gateway_type进行constantize以创建新对象,然后更新params:
payment_processor = "PaymentProcessors::#{new_gateway_type}".constantize.new(:account_id => self.id)
payment_processor.update_attributes(params[:payment_processor)
最后,我们保存新网关。如果此new_gateway的验证失败,我们通过对对象模型名称进行常量化并传入除type和id之外的新属性来恢复旧:
begin
payment_processor.save!
rescue ActiveRecord::RecordInvalid => invalid
original.type.constantize.new(original.attributes.except("id", "type")).save
end