我有一个遗留数据库(oracle),在该数据库中,我有几个表,它们包含不同的数据但结构相同。我不允许以任何方式更改数据库架构!
我想要一个DRY ActiveRecord模型从正确的表中获取正确的数据。问题是我需要动态覆盖self.table_name
才能使其正常工作。
这是我的代码:
ActiveRecord:基类,将由所有类似的表继承
class ListenLoc < ActiveRecord::Base
@@table_name = nil
def self.table_name
@@table_name
end
default_scope { where(validated: 1).where("URL_OK >= 0") }
scope :random_order, -> { order('DBMS_RANDOM.VALUE') }
scope :unvalidated, -> { unscope(:where).where(validated: 0) }
def self.get_category(cat_id)
where("cat_id = ?", cat_id)
end
def self.rand_sample(cat_id, lim)
where("cat_id = ?", cat_id).random_order.limit(lim)
end
end
子类看起来像这样:
A
class ListenLocA < ListenLoc
@@table_name = 'LISTEN_ADDR_A'
self.sequence_name = :autogenerated
belongs_to :category, class_name: 'ListenLocCatA', foreign_key: 'cat_id'
belongs_to :country, class_name: 'Country', foreign_key: 'country_id'
end
乙
class ListenLocB < ListenLoc
@@table_name = 'LISTEN_ADDR_B'
self.sequence_name = :autogenerated
belongs_to :category, class_name: 'ListenLocCatB', foreign_key: 'cat_id'
belongs_to :country, class_name: 'Country', foreign_key: 'country_id'
end
上述工作,但我已经注意到在进行特定的选择查找时存在一些缺陷。
这是一个好方法吗?有没有更好的方法动态传递self.table_name
?
更新
有人会认为这应该有效,但是我得到一个错误,即表不存在,因为ActiveRecord在创建Object之前尝试验证表,并且没有动态地在ListenLoc模型上设置self.table_name。
class ListenLocB < ListenLoc
self.table_name = 'LISTEN_ADDR_B'
self.sequence_name = :autogenerated
belongs_to :category, class_name: 'ListenLocCatB', foreign_key: 'cat_id'
belongs_to :country, class_name: 'Country', foreign_key: 'country_id'
end
答案 0 :(得分:2)
我意识到,我可以在不使用全局变量的情况下使用superclass
,我最终使用了它。与globals一样,这不会造成竞争条件问题。
class ListenLocB < ListenLoc
superclass.table_name = 'LISTEN_ADDR_B' # or ListenLoc.table_name
self.sequence_name = :autogenerated
belongs_to :category, class_name: 'ListenLocCatB', foreign_key: 'cat_id'
belongs_to :country, class_name: 'Country', foreign_key: 'country_id'
end
答案 1 :(得分:1)
在Ruby类中,变量在整个层次结构中共享,因此您的方法将无法工作。类变量背后的一般想法 - 除非你100%确定你知道自己在做什么,否则不要使用它。即使你是 - 有可能更好的方法。
至于实际问题 - 您使用table_name
所做的事情并未干涸,因为您添加了比您保存的更多行。而且,它使阅读更加困难。
刚刚放
self.table_name =
它应该存在于每个模型中 - 它简洁易读。
另一种选择是使用绑定到ListenLoc
类的本地化常量:
class ListenLoc
def self.table_name
TABLE_NAME
end
end
class ListenLocB < ListenLoc
::TABLE_NAME = 'LISTEN_ADDR_B'
end
为什么会这样?
我的理解如下:
通过编写::TABLE_NAME
,您可以在全局范围内定义常量TABLE_NAME
。
当您的呼叫传播到ListenDoc
类时,它会尝试解析常量ListenDoc::TABLE_NAME
,但它找不到它的范围。然后查看是否在外部范围中定义了常量TABLE_NAME
,并且它发现::TABLE_NAME
确实已定义,并且它的值为'LISTEN_ADDR_B'
。因此,有效。
我可能一直不清楚,因为我对该主题的理解仍然存在,但它肯定与Ruby搜索常量的方式有关。
这不是很直接,因为很少有警告(毕竟和所有人一样)。