Rails 4 — динамическое сопоставление модели с двумя разными таблицами базы данных

У меня есть многодоменное приложение, взаимодействующее с устаревшей базой данных. В этой БД у меня есть две таблицы с разными именами, назовем их USER_A и USER_B. Их структура и типы данных абсолютно одинаковы, единственное отличие состоит в том, что они получают свои данные из разных доменов.

Теперь я хотел бы иметь single scaffold (model/controller/view), который, в зависимости от домена, сопоставляется с правильной таблицей БД.

Домен A будет работать с моделью/контроллером с именем User, который внутренне сопоставляется с таблицей базы данных USER_A, а домен B будет работать с той же моделью/контроллером User, но сопоставляется с таблицей USER_B.

Я также хотел бы использовать resource :user в своих маршрутах для доступа к модели по рельсам.

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

Как это можно сделать с помощью Rails ActiveRecord?




Ответы (1)


У меня нет многотабличной БД, готовой для тестирования, так что это обоснованное предположение о решении:

# respective models
class User < ActiveRecord::Base
end

class DomainAUser < User
  self.table_name = "USER_A"
end

class DomainBUser < User
  self.table_name = "USER_B"
end

# controller
def set_user
  @user = if request.subdomain(0) == "DomainA" 
            DomainAUser.find(params[:id])
          else 
            DomainBUser.find(params[:id])
          end
end

Изменить: вот альтернативный фрагмент хакерства метапрограммирования, который создает экземпляр подкласса внутри самого родительского класса. Проверено и работает.

Я действительно не хотел бы поддерживать что-то вроде этого, хотя.

# model
class User < ActiveRecord::Base
  def self.for_domain(domain_suffix)
    class_eval "class DomainUser < User; self.table_name='user_#{domain_suffix}'; end"
    "User::DomainUser".constantize
  end
end

# controller
User.for_domain("a").new
person fylooi    schedule 30.07.2015
comment
Делая это так, вам не понадобится класс User. В настоящее время делаю это таким образом, однако он не очень сухой .... - person mahatmanich; 31.07.2015
comment
@mahatmanich: Единственное, что должно быть в подклассах, это вызов self.table_name. Все проверки/отношения/и т. д. должны идти в родительский класс User. Как вы считаете, что это не DRY? - person fylooi; 31.07.2015
comment
Я пытаюсь сделать так, чтобы одна модель, один контроллер отображались как ресурс в маршрутах. Что-то вроде псевдо MTI, поскольку я действительно должен использовать STI, но мне не разрешено изменять структуру БД. - person mahatmanich; 31.07.2015
comment
@mahatmanich: Не думаю, что возможно определить table_name после инициализации, поэтому вот альтернатива, которая делает это в самой модели. Но я бы порекомендовал первый способ. - person fylooi; 31.07.2015
comment
спасибо, что нашли время, чтобы помочь. В настоящее время я на самом деле пытаюсь заставить ваше первое предложение работать и посмотреть, как оно работает. - person mahatmanich; 31.07.2015
comment
Хорошо, я отказываюсь от этого, спасибо за помощь, но мне нужно будет использовать конструкцию MVC для каждой таблицы. Спасибо за помощь. - person mahatmanich; 31.07.2015