使用STI为rails中的子模型指定唯一属性

时间:2014-12-11 15:46:47

标签: ruby-on-rails ruby sti

我打算在Rails中使用STI,并使用以下模型:

class Promo < ActiveRecord::Base
end

class Event < Promo
end

class Discount < Promo
end

EventDiscount之间的属性只有一些差异所以我认为STI是一个很好的方法。

我不确定如何确保,例如 Event具有额外的image_filename属性。我知道它会在promos表格中,并且需要NULL - 如果我插入Discount行的话。 如何确保Discount对象对image_filename属性一无所知(即未在Discount.column_names中列出和/或无法设置它)并且Event知道它吗?

2 个答案:

答案 0 :(得分:4)

我认为概念不同,而您的Promo类继承自ActiveRecord::Base,所有子类都继承其属性,因此,列名在所有STI中共享。但您仍然可以在数据库中将列image_filename设置为null并在模型中对其进行验证,这样您就可以根据具体情况存储和更新记录:

class Promo < ActiveRecord::Base
  #Common validations
  validates :name, presence: true
  validates :user, presence: true
end

class Event < Promo
  #event related validations
  validates image_filename, presence: true
end

class Discount < Promo
  #discount related validations
  validates percentage, presence: true
end

这是一个很棒的教程http://samurails.com/tutorial/single-table-inheritance-with-rails-4-part-1/

答案 1 :(得分:1)

进行STI设置意味着您将为每个子类提供2种不同的表单。这意味着您将在表单中为EventDiscount提供相应的字段,因此当您保存Discount记录时,第二个image_file字段将具有nil默认值(如果您没有将其更改为其他内容)。

现在,Promo类的所有属性实际上都是您可以覆盖的方法,因此如果在Discount中您将执行此操作:

def additional_image_filename #name of the attribute
  nil
end

无论在那里保存什么,它总是会返回nil。基于我的应用程序的控制台中的示例,我覆盖了name属性(不是我需要的,只是显示):

2.1.5 :028 > Recipe.last
  Recipe Load (0.2ms)  SELECT  "recipes".* FROM "recipes"   ORDER BY "recipes"."id" DESC LIMIT 1
 => #<Recipe id: 4, name: "Potatoes Au Gratin", instructions: nil, created_at: "2014-12-04 12:54:26", updated_at: "2014-12-04 12:54:26"> 
2.1.5 :029 > Recipe.last.name
  Recipe Load (0.4ms)  SELECT  "recipes".* FROM "recipes"   ORDER BY "recipes"."id" DESC LIMIT 1
 => nil 

正如您所看到的,数据库中有一个名称,它有一个值,但它会返回nil,因为它被覆盖了。

此外,您可以为单独的类或回调添加验证,以确保清除您不需要的属性,如所谓的additional_image_filename,但我不明白为什么您需要这个。 。对于每个表单,我猜你会有单独的控制器和操作,因此会有不同的permitted_params,可以让你选择只允许保存哪些字段。

简而言之:

  1. 请勿在表单中加入additional_image_filename以获取折扣
  2. 请勿在{{1​​}}中的additional_image_filename中包含“折扣”的控制器操作
  3. 在db中将值设置为nil或覆盖peritted_params类中的方法,如上所述。