dynamic table names for Active Record models

后端 未结 7 1354
感情败类
感情败类 2021-01-27 13:25

I have an interesting Active Record problem and I\'m not quite sure what the cleanest solution is. The legacy database that I am integrating with has a strange wrinkle in its sc

相关标签:
7条回答
  • 2021-01-27 13:41

    DB table partitioning is pretty common practice really. I'd be surprised if someone hasn't done this before. How about ActsAsPartitionable? http://revolutiononrails.blogspot.com/2007/04/plugin-release-actsaspartitionable.html

    Another possibility: can your DBMS pretend that the partitions are one big table? I think MySQL supports this.

    0 讨论(0)
  • 2021-01-27 13:42

    Assumption 1: you know what type of car you're looking at, so you can tell if it's a ford or a dodge.

    Lets put the car make in an attribute called (of all things) make. You should normalize this later, but for now lets keep it simple.

    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
    

    You could add some protection in there to validate and downcase your iMake. You could also do something to ensure your table exists.

    Now writing to the table.. I'm not sure how that would work. I've only ever read from it. Perhaps its something simple too.

    0 讨论(0)
  • 2021-01-27 13:42

    Why not simply put all the wheels in one table and use a standard :has_many? You can do it in a migration:

    1. create new wheels table
    2. migrate data from other tables to the newly created table
    3. delete the old tables
    0 讨论(0)
  • 2021-01-27 13:46

    Here's a way you can do it. The basics (before the 70 lines of code) are:

    • create a has_many for each car type
    • define a method "wheels" that uses the table name in the association to get the right wheels

    Let me know if you have any questions

    #!/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
    
    0 讨论(0)
  • 2021-01-27 13:52

    I would do this association with custom function in model:

    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
    

    Maybe you can do it using association with :finder_sql, but I'm not sure how to pass arguments to it. I used model Wheel which you have to define if you want your data to be mapped by ActiveRecord. Probably you can make this model from one of your existing tables with wheels.

    And I didn't test it ;).

    0 讨论(0)
  • 2021-01-27 13:59

    How about this instead? (here's the gist: 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
    
    0 讨论(0)
提交回复
热议问题