在我的申请中,我有一个名为Budget的课程.预算可以是多种类型.例如,我们假设有两个预算:FlatRateBudget和HourlyRateBudget.两者都继承了课堂预算.
这是我到现在为止
class Budget < ActiveRecord::Base validates_presence_of :price end class FlatRateBudget < Budget end class HourlyRateBudget < Budget validates_presence_of :quantity end
在控制台中,如果我这样做:
b = HourlyRateBudget.new(:price => 10) b.valid? => false b.errors.full_messages => ["Quantity can't be blank"]
正如所料.
问题是STI上的“type”字段来自params ..所以我需要做一些像
b = Budget.new(:type => "HourlyRateBudget",:price => 10) b.valid? => true
这意味着rails在超类中运行验证,而不是在设置类型之后实例化子类.
我知道这是预期的行为,因为我正在实例化一个不需要数量字段的类,但是我想知道是否有任何方式告诉rails为子类而不是超级运行验证.
解决方法@H_404_22@
您可以使用自定义验证程序来解决此问题,类似于此问题的答案:
Two models,one STI and a Validation但是,如果您可以简单地实例化初始化的子类型,那么在这种情况下,您将完全避免使用自定义验证器.
如您所注意到的那样,单独设置类型字段并不会将实例从一种类型更改为另一种类型.虽然ActiveRecord将使用类型字段在从数据库读取对象时实例化正确的类,但以另一种方式进行操作(实例化超类,然后手动更改类型字段)不具有更改对象类型的效果你的应用程序正在运行 – 它不会这样工作.
另一方面,自定义验证方法可以独立检查类型字段,实例化相应类型的副本(基于类型字段的值),然后运行.valid?在该对象上,导致子类的验证以似乎是动态的方式运行,即使它实际上是在进程中创建适当子类的实例.
如您所注意到的那样,单独设置类型字段并不会将实例从一种类型更改为另一种类型.虽然ActiveRecord将使用类型字段在从数据库读取对象时实例化正确的类,但以另一种方式进行操作(实例化超类,然后手动更改类型字段)不具有更改对象类型的效果你的应用程序正在运行 – 它不会这样工作.
另一方面,自定义验证方法可以独立检查类型字段,实例化相应类型的副本(基于类型字段的值),然后运行.valid?在该对象上,导致子类的验证以似乎是动态的方式运行,即使它实际上是在进程中创建适当子类的实例.