Ruby on rails 活动记录模型的动态表名称

Ruby on rails 活动记录模型的动态表名称,ruby-on-rails,activerecord,Ruby On Rails,Activerecord,我有一个有趣的活动记录问题,我不太确定是什么 最干净的解决方案是。我正在与之集成的遗留数据库 在它的模式中有一个奇怪的折痕,其中有一个逻辑表 “分区”为多个物理表。每张桌子都有相同的颜色 结构,但包含有关不同项的数据 我不擅长清楚地解释这一点(你可以看出!)。让我试试 并举例说明。假设我们有一辆车,它有 一个或多个轮子。通常我们会用一张汽车表和一个 如下所示: CREATE TABLE cars ( `id` int(11) NOT NULL auto_increment, `name`

我有一个有趣的活动记录问题,我不太确定是什么 最干净的解决方案是。我正在与之集成的遗留数据库 在它的模式中有一个奇怪的折痕,其中有一个逻辑表 “分区”为多个物理表。每张桌子都有相同的颜色 结构,但包含有关不同项的数据

我不擅长清楚地解释这一点(你可以看出!)。让我试试 并举例说明。假设我们有一辆车,它有 一个或多个轮子。通常我们会用一张汽车表和一个 如下所示:

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)
)
这里我们有一套用于桌子的轮子,还有 car\u to_wheel\u table\u map table包含从car\u id到 包含特定汽车车轮的特定车轮。如果我 要想找到一套汽车的轮子,我首先要找出哪一套 车轮表使用通过汽车\u到\u车轮表\u地图表,然后查看 在car_to_wheel_table_映射中指定的车轮表中的up记录

首先,有人能告诉我是否有一个标准的名字吗 这个技巧

第二,有没有人对我如何使这项工作在 以一种非常干净的方式进行活动记录。在我看来,我可以 Wheel模型,其中可以为每个实例定义表名,或者我可以 在运行时使用正确的表名动态创建模型类 如映射表中所指定


编辑:请注意,将架构更改为更接近AR想要的内容不是一个选项。各种遗留代码库都依赖于此模式,无法实际修改。

为什么不简单地将所有控制盘放在一个表中,并使用一个标准:has\u many?您可以在迁移中执行此操作:

  • 创建新的控制盘表
  • 将数据从其他表迁移到新创建的表
  • 删除旧表

  • DB表分区实际上是非常常见的做法。如果有人以前没有这样做过,我会感到惊讶。ActsAsPartitionable怎么样


    另一种可能性:您的DBMS可以假装分区是一个大表吗?我认为MySQL支持这一点。

    我将在模型中与自定义函数进行关联:

    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\u sql的关联来实现,但我不确定如何将参数传递给它。我使用了ModelWheel,如果希望ActiveRecord映射数据,则必须定义它。可能您可以从现有的带轮子的表中创建此模型


    我没有测试它;)

    这里有一个方法可以做到这一点。基本(在70行代码之前)是:

    • 为每种车型创建一个
    • 定义一个方法“wheels”,该方法使用关联中的表名来获取正确的控制盘
    如果你有任何问题,请告诉我

    #!/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
    
    #/usr/bin/env ruby
    %w | rubygems active | u record irb |。每个{| lib | require lib}
    ActiveSupport::拐点。拐点。单数(“toyota”、“toyota”)
    汽车类型=%w |福特别克丰田|
    ActiveRecord::Base.logger=logger.new(标准输出)
    ActiveRecord::Base.build\u连接(
    :adapter=>“sqlite3”,
    :数据库=>“:内存:”
    )
    ActiveRecord::Schema.define do
    创建表格:汽车不需要|
    t、 字符串:名称
    结束
    创建表:汽车到车轮表映射,:id=>false do | t|
    t、 整数:车号
    t、 字符串:轮_表
    结束
    汽车类型。每个都有汽车类型|
    创建表格“wheels(车轮){car#u type.pluralize}”do|t|
    t、 整数:车号
    t、 字符串:颜色
    结束
    结束
    结束
    汽车类型。每个都有汽车类型|
    评估“Rav4”)
    rav4.创建汽车车轮地图(:车轮表格=>“丰田”)
    rav4.wheels.create(:color=>“red”)
    嘉年华=汽车。创建(:name=>“嘉年华”)
    嘉年华。创建汽车车轮地图(:车轮表格=>“福特”)
    fiesta.wheels.create(:color=>“green”)
    IRB.start如果文件=0美元
    
    这个怎么样?(要点如下:)

    #/usr/bin/env ruby
    %w | rubygems active | u record irb |。每个{| lib | require lib}
    ActiveSupport::拐点。拐点。单数(“toyota”、“toyota”)
    ActiveRecord::Base.logger=logger.new(标准输出)
    ActiveRecord::Base.build\u连接(
    :adapter=>“sqlite3”,
    :数据库=>“:内存:”
    )
    ActiveRecord::Schema.define do
    创建表格:汽车不需要|
    t、 字符串:名称
    结束
    创建表:汽车到车轮表映射,:id=>false do | t|
    t、 整数:车号
    t、 字符串:轮_表
    结束
    创建表格:fords do的车轮|
    t、 整数:车号
    t、 字符串:颜色
    结束
    创建表格:丰田汽车的车轮|
    t、 整数:车号
    t、 字符串:颜色
    结束
    结束
    类Wheel:汽车车轮图
    def车轮
    @车轮| |=开始
    _klass=“#{wheel_table.classify}wheel”
    评估“丰田”)
    嘉年华=汽车。创建(:name=>“嘉年华”)
    嘉年华。创建汽车车轮地图(:车轮表格=>“福特”)
    rav4.wheels.create(:color=>“red”)
    fiesta.wheels.create(:color=>“green”)
    #IRB.start如果文件=0美元
    
    很遗憾,我知道你的麻烦。任何想在你的数据库中划分表的人一定是被你的老板解雇了,然后被我雇用了;-)

    无论如何,解决方案(通过RailsForum):

    就是使用尼克博士的魔法模型


    干杯假设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
    
    创建表车(
    `id`int(11)非空自动增量,
    `名称`varchar(255),
    “make”varchar(255),
    #ect
    )
    类WheelCREATE 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