我有一个有趣的Active Record问题,我不太清楚是什么 最干净的解决方案是。我正在集成的遗留数据库 在其架构中有一个奇怪的皱纹,其中有一个逻辑表 '分区'到几个物理表。每张桌子都一样 结构,但包含有关不同项目的数据。
我不太清楚解释这个问题(正如你所知道的那样!)。让我尝试 并用一个具体的例子来解释。假设我们有一辆车,它有 一个或多个轮子。通常我们用Car表和a表示 车轮表如此:
CREATE TABLE cars (
`id` int(11) NOT NULL auto_increment,
`name` varchar(255),
;etc
)
CREATE TABLE wheels (
`id` int(11) NOT NULL auto_increment,
`car_id` int(11) NOT NULL,
`color` varchar(255),
;etc
)
到目前为止,这么好。但是,我的遗产中的“分配”策略 数据库看起来更像是:
CREATE TABLE cars (
`id` int(11) NOT NULL auto_increment,
`name` varchar(255),
;etc
)
CREATE TABLE car_to_wheel_table_map (
`car_id` int(11) NOT NULL,
`wheel_table` varchar(255)
)
CREATE TABLE wheels_for_fords (
`id` int(11) NOT NULL auto_increment,
`car_id` int(11) NOT NULL,
`color` varchar(255)
)
CREATE TABLE wheels_for_buicks (
`id` int(11) NOT NULL auto_increment,
`car_id` int(11) NOT NULL,
`color` varchar(255)
)
CREATE TABLE wheels_for_toyotas (
`id` int(11) NOT NULL auto_increment,
`car_id` int(11) NOT NULL,
`color` varchar(255)
)
所以这里我们有一组wheels_for_x表和一个 car_to_wheel_table_map表,其中包含从car_id到。的映射 特定的wheels_for_x,包含特定汽车的车轮。如果我 想要找到一辆车的轮子我首先要找出哪一个 要通过car_to_wheel_table_map表使用wheel表,然后查看 up car_to_wheel_table_map中指定的wheel表中的记录。
首先,有人可以告诉我是否有标准名称 这种技术?
其次,有没有人对如何使这项工作有任何指示 积极的记录以一种干净的方式。我看到它的方式我可以有一个 可以在每个实例中定义表名的轮模型,或者我可以 使用正确的表名在运行时动态创建Model类 如映射表中所指定。
编辑:请注意,将模式更改为更接近AR所需的模式不是一种选择。各种遗留代码库依赖于此模式,无法实际修改。
答案 0 :(得分:2)
DB表分区是非常常见的做法。如果有人之前没有这样做,我会感到惊讶。 ActsAsPartitionable怎么样? http://revolutiononrails.blogspot.com/2007/04/plugin-release-actsaspartitionable.html
另一种可能性:您的DBMS可以假装分区是一个大表吗?我认为MySQL支持这一点。
答案 1 :(得分:1)
这是你可以做到的一种方式。基础知识(在70行代码之前)是:
如果您有任何问题,请告诉我
#!/usr/bin/env ruby
%w|rubygems active_record irb|.each {|lib| require lib}
ActiveSupport::Inflector.inflections.singular("toyota", "toyota")
CAR_TYPES = %w|ford buick toyota|
ActiveRecord::Base.logger = Logger.new(STDOUT)
ActiveRecord::Base.establish_connection(
:adapter => "sqlite3",
:database => ":memory:"
)
ActiveRecord::Schema.define do
create_table :cars do |t|
t.string :name
end
create_table :car_to_wheel_table_map, :id => false do |t|
t.integer :car_id
t.string :wheel_table
end
CAR_TYPES.each do |car_type|
create_table "wheels_for_#{car_type.pluralize}" do |t|
t.integer :car_id
t.string :color
end
end
end
CAR_TYPES.each do |car_type|
eval <<-END
class #{car_type.classify}Wheel < ActiveRecord::Base
set_table_name "wheels_for_#{car_type.pluralize}"
belongs_to :car
end
END
end
class Car < ActiveRecord::Base
has_one :car_wheel_map
CAR_TYPES.each do |car_type|
has_many "#{car_type}_wheels"
end
delegate :wheel_table, :to => :car_wheel_map
def wheels
send("#{wheel_table}_wheels")
end
end
class CarWheelMap < ActiveRecord::Base
set_table_name "car_to_wheel_table_map"
belongs_to :car
end
rav4 = Car.create(:name => "Rav4")
rav4.create_car_wheel_map(:wheel_table => "toyota")
rav4.wheels.create(:color => "red")
fiesta = Car.create(:name => "Fiesta")
fiesta.create_car_wheel_map(:wheel_table => "ford")
fiesta.wheels.create(:color => "green")
IRB.start if __FILE__ == $0
答案 2 :(得分:1)
相反怎么样? (这是要点:http://gist.github.com/111041)
#!/usr/bin/env ruby
%w|rubygems active_record irb|.each {|lib| require lib}
ActiveSupport::Inflector.inflections.singular("toyota", "toyota")
ActiveRecord::Base.logger = Logger.new(STDOUT)
ActiveRecord::Base.establish_connection(
:adapter => "sqlite3",
:database => ":memory:"
)
ActiveRecord::Schema.define do
create_table :cars do |t|
t.string :name
end
create_table :car_to_wheel_table_map, :id => false do |t|
t.integer :car_id
t.string :wheel_table
end
create_table :wheels_for_fords do |t|
t.integer :car_id
t.string :color
end
create_table :wheels_for_toyotas do |t|
t.integer :car_id
t.string :color
end
end
class Wheel < ActiveRecord::Base
set_table_name nil
belongs_to :car
end
class CarWheelMap < ActiveRecord::Base
set_table_name "car_to_wheel_table_map"
belongs_to :car
end
class Car < ActiveRecord::Base
has_one :car_wheel_map
delegate :wheel_table, :to => :car_wheel_map
def wheels
@wheels ||= begin
the_klass = "#{wheel_table.classify}Wheel"
eval <<-END
class #{the_klass} < ActiveRecord::Base
set_table_name "wheels_for_#{wheel_table.pluralize}"
belongs_to :car
end
END
self.class.send(:has_many, "#{wheel_table}_wheels")
send "#{wheel_table}_wheels"
end
end
end
rav4 = Car.create(:name => "Rav4")
rav4.create_car_wheel_map(:wheel_table => "toyota")
fiesta = Car.create(:name => "Fiesta")
fiesta.create_car_wheel_map(:wheel_table => "ford")
rav4.wheels.create(:color => "red")
fiesta.wheels.create(:color => "green")
# IRB.start if __FILE__ == $0
答案 3 :(得分:0)
我会在模型中使用自定义函数进行此关联:
has_one :cat_to_wheel_table_map
def wheels
Wheel.find_by_sql("SELECT * FROM #{cat_to_wheel_table_map.wheel_table} WHERE car_id == #{id}")
end
也许你可以使用与finder_sql的关联来做到这一点,但我不确定如何将参数传递给它。我使用了模型Wheel,你必须定义是否希望你的数据由ActiveRecord映射。也许你可以从现有的一个带轮子的桌子上制作这个模型。
我没有测试它;)。
答案 4 :(得分:0)
无论如何,解决方案(通过RailsForum): http://railsforum.com/viewtopic.php?id=674
是使用Dr. Nic的魔法模型。
干杯
答案 5 :(得分:0)
假设1:你知道你正在看什么类型的汽车,所以你可以判断它是福特还是闪避。
让我们把汽车制造在一个名为(所有东西)的属性中。 你应该稍后将其标准化,但是现在让我们保持简单。
CREATE TABLE cars (
`id` int(11) NOT NULL auto_increment,
`name` varchar(255),
'make' varchar(255),
#ect
)
class Wheel < ActiveRecord::Base
def find_by_make(iMake)
select("wheels_for_#{iMake}.*").from("wheels_for_#{iMake}");
end
#...
end
您可以在其中添加一些保护来验证和缩小您的iMake。 您还可以采取措施确保您的表存在。
现在写到桌子上......我不确定这是怎么回事。我只读过它。 也许它也很简单。
答案 6 :(得分:-2)
为什么不简单地将所有轮子放在一个表中并使用标准:has_many?您可以在迁移中执行此操作: