динамические имена таблиц для моделей Active Record - PullRequest
0 голосов
/ 13 мая 2009

У меня есть интересная проблема Active Record, и я не совсем уверен, что самое чистое решение. Устаревшая база данных, с которой я интегрирую имеет странную складку в своей схеме, где одна логическая таблица была «разделен» на несколько физических таблиц. Каждая таблица имеет одинаковый структура, но содержит данные о разных предметах.

Я не очень хорошо объясняю это ясно (как вы можете сказать!). Дай мне попробовать и объяснить на конкретном примере. Допустим, у нас есть автомобиль, который имеет одно или несколько колес. Обычно мы представляем это с таблицей автомобилей и Колеса настольные вроде так:

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)
)

Итак, у нас есть набор таблиц wheel_for_x и таблица car_to_wheel_table_map, которая содержит отображение из car_id в конкретный wheel_for_x, который содержит колеса для конкретного автомобиля. Если я хочу найти комплект колес для автомобиля я сначала должен выяснить какие колесные диски для использования через таблицу car_to_wheel_table_map, а затем посмотрите количество записей в таблице колес, указанной в car_to_wheel_table_map.

Во-первых, может ли кто-нибудь просветить меня, если есть стандартное название эта техника?

Во-вторых, есть ли у кого-нибудь указания на то, как я могу заставить эту работу работать в Активная запись в хорошем чистом виде. То, как я это вижу, я могу иметь Модель колеса, где имя таблицы может быть определено для каждого экземпляра, или я могу динамически создавать классы Model во время выполнения с правильным именем таблицы как указано в таблице сопоставления.

РЕДАКТИРОВАТЬ : Обратите внимание, что изменение схемы, чтобы быть ближе к тому, что хочет AR, не вариант. Различные устаревшие кодовые базы опираются на эту схему и не могут быть реально изменены.

Ответы [ 7 ]

2 голосов
/ 13 мая 2009

Разделение таблиц БД - довольно распространенная практика. Я был бы удивлен, если бы кто-то не делал этого раньше. Как насчет ActsAsPartitionable? http://revolutiononrails.blogspot.com/2007/04/plugin-release-actsaspartitionable.html

Другая возможность: может ли ваша СУБД сделать вид, что разделы представляют собой одну большую таблицу? Я думаю, что MySQL поддерживает это.

1 голос
/ 14 мая 2009

Как насчет этого вместо этого? (вот суть: 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
1 голос
/ 13 мая 2009

Вот способ, которым вы можете сделать это. Основы (до 70 строк кода):

  • создать has_many для каждого типа автомобиля
  • определить метод "колеса", который использует имя таблицы в ассоциации, чтобы получить правильные колеса

Дайте мне знать, если у вас есть какие-либо вопросы

#!/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 голосов
/ 30 мая 2012

Предположение 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. Вы также можете сделать что-то, чтобы убедиться, что ваша таблица существует.

Теперь пишу в таблицу ... Я не уверен, как это будет работать. Я только когда-либо читал из этого. Возможно, это тоже что-то простое.

0 голосов
/ 28 июня 2009

К сожалению, я знаю твои проблемы. Кто бы ни думал, что разделить таблицы в вашей базе данных должен быть уволен вашим начальником и нанят моим; -)

В любом случае, решение (через RailsForum): http://railsforum.com/viewtopic.php?id=674

- использовать магические модели доктора Ника.

Приветствия

0 голосов
/ 13 мая 2009

Я бы сделал эту связь с пользовательской функцией в модели:

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. Вероятно, вы можете сделать эту модель из одного из ваших существующих столов с колесами.

И я не проверял это;).

0 голосов
/ 13 мая 2009

Почему бы просто не положить все колеса в один стол и использовать стандарт: has_many? Вы можете сделать это в процессе миграции:

  1. создать новую таблицу колес
  2. перенос данных из других таблиц во вновь созданную таблицу
  3. удалить старые таблицы
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...