代码之家  ›  专栏  ›  技术社区  ›  Pete Hodgson

活动记录模型的动态表名

  •  0
  • Pete Hodgson  · 技术社区  · 16 年前

    我有一个有趣的活动记录问题,我不太确定 最干净的解决方案是。我正在与之集成的遗留数据库 “分区”成几个物理表。每张桌子都一样 结构,但包含有关不同项的数据。

    我不擅长解释清楚(你可以说!).让我试试 一个或多个轮子。通常我们会用汽车桌和

    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_to_wheel_table_map table包含从car_id到 想找到一辆车的车轮组我首先得找出 车轮表通过汽车使用车轮表,然后查看 在“汽车到车轮表”地图中指定的车轮表中的向上记录。

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

    第二,有没有人能告诉我怎么做 可以为每个实例定义表名的轮子模型,或者我可以 在运行时使用正确的表名动态创建模型类

    编辑 :请注意,将模式更改为更接近AR所需的模式不是一个选项。各种遗留代码基依赖于此模式,无法实际修改。

    7 回复  |  直到 16 年前
        1
  •  2
  •   Simon Woodside    16 年前

    DB表分区实际上是非常常见的实践。如果有人以前没做过,我会很惊讶的。行动可分割如何? http://revolutiononrails.blogspot.com/2007/04/plugin-release-actsaspartitionable.html

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

        2
  •  1
  •   Erik Kastner    16 年前

    这是一个你能做到的方法。基本(70行代码之前)是:

    • 为每种车型创建一个has_many
    • 定义一个方法“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
    
        3
  •  1
  •   Erik Kastner    16 年前

    不如这样吧?(要点如下: 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
    
        4
  •  0
  •   klew    16 年前

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

    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
    

    也许您可以使用association with:finder_sql来完成,但我不确定如何向它传递参数。我使用了model Wheel,如果您希望通过ActiveRecord映射数据,则必须定义它。也许你可以从你现有的一个表格中用车轮做这个模型。

        5
  •  0
  •   JayTeeSr JayTeeSr    15 年前

    可悲的是,我知道你的烦恼。谁想在你的数据库中划分表,一定是被你老板炒了鱿鱼,被我雇了;-)

    无论如何,解决方案(通过RailsForum): http://railsforum.com/viewtopic.php?id=674

    干杯

        6
  •  0
  •   baash05    13 年前

    假设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。 您还可以做一些事情来确保您的表存在。

    现在给桌子写信。。我不知道那会怎样。我只看过这本书。

        7
  •  -2
  •   Jon Smock    16 年前

    为什么不干脆把所有的轮子放在一张桌子上,用一个标准:有很多吗?您可以在迁移中执行此操作:

    1. 创建新的控制盘表