Rails:STI处理实体子类型的最佳方法?

时间:2014-02-25 11:02:47

标签: ruby-on-rails ruby

我有一个实体Order,它由两个子实体SalesOrderPurchaseOrder组成。这两个子实体共享其行为的95%。

单表继承和代码如何最好地接近这个或者有更好的方法吗?

class Order < ActiveRecord::Base
  belongs_to :order_type
end

class SalesOrder < Order       
  before_validation(on: :create) do
    self.order_type = OrderType.find_by!(name: "Sales order")
  end
  validates_inclusion_of :order_type_id, in: [OrderType.find_by!(name: 'Sales order').id]  

  validate :potential_savings, numericality: true, allow_nil: true

  default_scope { joins(:order_type).where("order_types.name = ?", "Sales order") }

  # OrderItem also has two sub entities (SalesOrderItem and PurchaseOrderItem with slightly different behaviour)
  has_many :order_items, class_name: "SalesOrderItem", foreign_key: "order_id", inverse_of: :order  
end

class PurchaseOrder < Order       
  before_validation(on: :create) do
    self.order_type = OrderType.find_by!(name: "Purchase order")
  end
  validates_inclusion_of :order_type_id, :in => [OrderType.find_by!(name: 'Purchase order').id]  

  validates_inclusion_of :potential_savings, :in => [nil], allow_nil: true # throw error if not nil

  default_scope { joins(:order_type).where("order_types.name = ?", "Purchase order") }

  # OrderItem also has two sub entities (SalesOrderItem and PurchaseOrderItem with slightly different behaviour)
  has_many :order_items, class_name: "PurchaseOrderItem", foreign_key: "order_id", inverse_of: :order
end

2 个答案:

答案 0 :(得分:1)

我不知道您的数据模型的所有细节,但也许您可以将共享功能放入Concern

类似的东西:

module Orderable
  extend ActiveSupport::Concern

  included do
    cattr_reader :order_type

    # All common/shared relations and validations are defined here
    belongs_to :order_type

    before_validation(on: :create) do
      self.order_type = OrderType.find_by!(name: @@order_type)
    end

    validates_inclusion_of :order_type_id, in: [OrderType.find_by!(name: @@order_type).id] 

    default_scope { joins(:order_type).where("order_types.name = ?", @@order_type) }
  end

  module ClassMethods
    def set_order_type(order_type)
      self.class_variable_set("@@order_type", order_type)
    end
  end
end

class SalesOrder < ActiveRecord::Base
  # Include the concern and call a method on the concern to set the order_type
  include Orderable
  set_order_type 'Sales order'

  # This validation is too different from the corresponding PurchaseOrder validation
  # so it is put in the model and not in the Orderable concern
  validate :potential_savings, numericality: true, allow_nil: true
end

class PurchaseOrder < ActiveRecord::Base
  include Orderable
  set_order_type 'Purchase order'

  # This validation is too different from the corresponding PurchaseOrder validation
  # so it is put in the model and not in the Orderable concern
  validates_inclusion_of :potential_savings, :in => [nil], allow_nil: true # throw error if not nil
end

不确定这是否更强大或DRY然后采用STI方式。此外,由于order_type名称的格式不同,最后的验证尚未实现,但可以修复。

我希望这会有所帮助或给予一些启发。

答案 1 :(得分:1)

SalesOrder 订单。所以,我认为继承是最好的方法。如果两种类型具有相同或几乎相同的数据,则STI是最佳策略。如果您觉得这种方法会创建很多空列,我会建议尝试不同的策略,但保留继承。

相关问题