2

別の結合モデル AccountOwnership を介して、2 つのモデル User と CustomerAccount の間の has_many :through 関係をセットアップしようとしています (users と account_ownerships テーブルは 1 つのデータベースにあり、db1 と言い、customer_accounts テーブルはリモート db にあり、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::エラー: テーブル
#'db2.account_ownerships' が存在しません: SELECT `customers`.* FROM
#`customers` INNER JOIN `account_ownerships` ON `customers`.id =
#`account_ownerships`.company_id WHERE ((`account_ownerships`.user_id
= 4))

ここでの問題は、「account_ownerships」および「users」テーブルが 1 つのデフォルト データベース (db1 など) に含まれており、「customers」テーブルが別のデータベース (db2 など) に含まれていることです。データベースへの接続は適切に構成されていますが、ルックアップ中に使用可能なデータベース接続オブジェクトが 1 つしかないため、Rails は db2 で account_ownerships データベースを見つけようとして失敗します。

同じ db 接続を使用して 2 つの異なるデータベースに接続する方法が見つからないため、設計/ロジックに欠陥があるように見えますが、設計を変更せずに回避策があるかどうかを確認したいと思います。(db2 は私の管理下にないため、設計を変更するのは気が進まない)

account_ownerships テーブルを db2 に移動することでこの問題を回避できるようですが、これは少なくとも私にとっては理想的ではありません。

Railsでこの関連付けをセットアップするための代替メカニズム/スキーマはありますか?

前もって感謝します。M

4

1 に答える 1

1

解決:

これは、生の SQL を含むすべてのデータベース アクセス メカニズムのコア制限であるため、Rails 関連付けマジックでは実現できないようです。

この問題を回避するために私がしたことは次のとおりです。

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 モデルに 2 つのインスタンス メソッドを追加する必要があります。

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 テーブルに反映されず、users メソッドで醜い ActiveRecord::RecordNotFound エラーが発生する可能性があります。

于 2010-09-29T23:17:05.600 に答える