Rails: настройка связи has_many_through между таблицами из разных баз данных - PullRequest
2 голосов
/ 30 сентября 2010

Я пытаюсь настроить has_many: через отношения между двумя моделями User и CustomerAccount через другую модель соединения AccountOwnership (таблицы users и account_ownerships находятся в одном БД, скажем, db1, а таблица customer_accounts - в удаленном БД, скажем, db2).

Вот соответствующий код, который устанавливает ассоциации

class User < ActiveRecord::Base
  has_many :account_ownerships, :dependent => :destroy
  has_many :companies, :through => :account_ownerships
end



class AccountOwnership < ActiveRecord::Base
  belongs_to :user
  belongs_to :company, :class_name => "Reporting::CustomerAccount"
end


class CustomerAccount < Reporting::Base
  set_table_name 'customers'
  establish_connection("db2_#{RAILS_ENV}")
end

config / database.yml (конфигурация правильная, хотя здесь не показана)

development:
  reconnect: false
  database: db1
  pool: 5

db2_development:
  reconnect: false
  database: db2
  host: different.host
  pool: 5

В скрипте / консоли

a = AccountOwnership.new(:user_id => 2, :company_id => 10)

a.user ## Returns the correct user

a.company ## returns the correct CustomerAccount instance

также

a.user.account_ownership ## returns a as anticipated

, но

a.user.companies ## produces the following error:
#ActiveRecord::StatementInvalid: Mysql::Error: Table
#'db2.account_ownerships' doesn't exist: SELECT `customers`.* FROM
#`customers`  INNER JOIN `account_ownerships` ON `customers`.id =
#`account_ownerships`.company_id    WHERE ((`account_ownerships`.user_id
= 4))

Проблема заключается в том, что таблицы "account_ownerships" и "users"содержатся в одной базе данных по умолчанию (скажем, db1), а таблица «клиенты» содержится в другой базе данных (скажем, db2).Соединения с базами данных настроены правильно, но во время поиска, так как доступен только один объект соединения с базой данных, Rails пытается найти базу данных account_ownerships в db2 и, следовательно, не удается.

Похоже, мой дизайн / логика можетбыть ошибочным, потому что я не могу найти способ подключения к двум разным базам данных, используя одно и то же соединение с БД, но я был бы рад увидеть, есть ли обходной путь, не меняя дизайн.(Я не хочу менять дизайн, потому что db2 не находится под моим контролем)

Похоже, что я могу обойти эту проблему, переместив свою таблицу account_ownerships в db2, но это не идеально для меня, по крайней мере.

Существуют ли альтернативные механизмы / схемы для установки этой ассоциации в Rails.

Заранее спасибо.M

1 Ответ

1 голос
/ 30 сентября 2010

Решение:

Кажется, что это не может быть достигнуто с помощью какой-либо магии ассоциации с Rails, поскольку это основное ограничение любого механизма доступа к базе данных, включая сырой SQL.

Вот что я сделал сОбойти проблему:

class User < ActiveRecord::Base
  has_many :account_ownerships, :dependent => :destroy

  def companies
    (account_ownerships.collect { |r| Reporting::CustomerAccount.find(r.company_id) }).flatten        
  end    
end

Это обеспечивает правильное приближение, как показано:

a = AcccountOwnership.create!(:user_id => 10, :company_id => 10)
u = User.find(10)
u.account_ownerships ### will return the correct account_ownership instance

ТАКЖЕ

u.companies ### will return a list of all companies enlisted for each account

И нам нужно добавить два метода экземпляра вМодель account_ownership, чтобы приблизить поведение ассоциации

class CustomerAccount < ActiveRecord::Base
  set_table_name "customers"        

  ########################################################
  ## This cannot be used because, customers and
  ## account_ownerships tables are contained in
  ## different databases, because of this it is 
  ## impossible to query these two tables from a
  ## single db connection, which is what we are
  ## attempting to achieve here.
  ## has_many :account_ownerships, :dependent => :destroy
  ########################################################

  def account_ownerships
    AccountOwnership.find(:all, :conditions => ["company_id = ?", self.id])
  end

  def users
    (account_ownerships.collect { |r| User.find(r.user_id) }).flatten
  end
end

Теперь мы можем сделать

c = CustomerAccount.find(10)
c.account_ownerships ## will return the right ownership accounts

И

c.users ## will iterate over all the accounts accumulating any users

ВНИМАНИЕ: 1. Поскольку каскадное удаление отсутствуетСделано в модели CustomerAccount, если какие-либо учетные записи будут удалены, это не будет отражено в таблице account_ownership, поэтому это может привести к появлению уродливых ошибок ActiveRecord :: RecordNotFound в методе users.

...