我很难通过has_many :through
关联访问数据,其中一些表位于单独的数据库中。
# database_one
class Input < ApplicationRecord
belongs_to :user # Works great
end
# database_two
class User < AbstractClass
belongs_to :group # Works great
has_many :inputs # Works great
end
# database_two
class Group < AbstractClass
has_many :users # Works great
has_many :inputs, through: :users # Does not work at all
end
class AbstractClass < ApplicationRecord
self.abstract_class = true
establish_connection "database_two_#{Rails.env}".to_sym
end
因此,使用上面的代码,我可以执行以下操作:
Group.first
=> #<Group id: 1...
User.first
=> #<User id: 1...
User.first.inputs
=> #<ActiveRecord::Associations::CollectionProxy []>
Group.first.users
=> #<ActiveRecord::Associations::CollectionProxy []>
但它不允许我做以下事情:
Group.first.inputs
ActiveRecord::StatementInvalid: PG::UndefinedTable: ERROR: relation "users" does not exist
LINE 1: SELECT "inputs".* FROM "inputs" INNER JOIN "users" ON "inpu...
^
: SELECT "inputs".* FROM "inputs" INNER JOIN "users" ON "inputs"."user_id" = "users"."id" WHERE "users"."group_id" = $1 LIMIT $2
看起来不可能在两个数据库中执行INNER JOIN
?我可以采取哪些措施来缓解这个问题吗?我已尝试将此方法添加到AbstractClass
但不幸的是它没有解决任何问题:
def self.table_name_prefix
"database_two_#{Rails.env}."
end
作为一种解决方法,我在群组模型中添加了以下内容,但这不是我正在寻找的解决方案。
def inputs
Input.where(id: users.ids)
end
答案 0 :(得分:4)
我认为在一个查询中不能连接两个不同的表。你可以做的就是使用Ruby来获得你的最终收藏。 从一个DB获取一个查询的集合,然后从另一个查询获取另一个集合。 然后使用Ruby从这两个集合中选择/过滤。我希望这会对你有所帮助。
答案 1 :(得分:0)
这就是我这样做的方式(假设两个数据库都在同一主机上运行):
1 / 为您的第二个数据库创建一个database.yml文件 这很重要,因为它允许您以Rails方式连接到第二个数据库。我相信你已经有了它的设置但是对于未来的开发人员绊倒这个问题,它可以按照以下方式完成:
<强>配置/ database.yml的强>
development:
adapter: postgresql
encoding: unicode
database: database_one_development
pool: 5
username: USERNAME
password: PASSWORD
test:
adapter: postgresql
encoding: unicode
database: database_one_test
pool: 5
username: USERNAME
password: PASSWORD
production:
adapter: postgresql
encoding: unicode
database: database_one_production
pool: 5
username: USERNAME
password: PASSWORD
<强>配置/ database_two.yml 强>
development:
adapter: postgresql
encoding: unicode
database: database_two_development
pool: 5
username: USERNAME
password: PASSWORD
test:
adapter: postgresql
encoding: unicode
database: database_two_test
pool: 5
username: USERNAME
password: PASSWORD
production:
adapter: postgresql
encoding: unicode
database: database_two_production
pool: 5
username: USERNAME
password: PASSWORD
<强>配置/初始化/ database_connector.rb 强>
DATABASE_ONE_DB = YAML.load_file(File.join(Rails.root, "config", "database.yml"))[Rails.env.to_s]
DATABASE_TWO_DB = YAML.load_file(File.join(Rails.root, "config", "database_two.yml"))[Rails.env.to_s]
2 / 将AbstractClass设置如下:
class DatabaseTwoModel < ApplicationRecord
self.abstract_class = true
establish_connection DATABASE_TWO
def self.table_name_prefix
"database_two_#{Rails.env}."
end
end
您的模型如下:
class User < DatabaseTwoModel
belongs_to :group
has_many :inputs
end
class Group < DatabaseTwoModel
has_many :users
has_many :inputs, through: :users
end
3 / 为避免混淆,我为属于database_one的模型创建了另一个类
class DatabaseOneModel < ApplicationRecord
self.abstract_class = true
def self.table_name_prefix
"database_one_#{Rails.env}."
end
end
您的Input
模型应该继承此类:
class Input < DatabaseOneModel
belongs_to :user # Works great
end
它本身确实很好用,但是在进行内部连接时,它可能会搞乱SQL查询。
4 / Rspec
如果您使用的是Rspec,则需要将其添加到rails_helper.rb
文件中:
database_one = Rails.configuration.database_configuration[Rails.env]
database_two = YAML.load_file(File.join(Rails.root, "config", "database_two.yml"))[Rails.env]
# start by truncating all the tables but then use the faster
# transaction strategy the rest of the time.
config.before(:suite) do
ActiveRecord::Base.establish_connection database_two
DatabaseCleaner.clean_with(:truncation)
DatabaseCleaner.strategy = :transaction
ActiveRecord::Base.establish_connection database_one
DatabaseCleaner.clean_with(:truncation)
DatabaseCleaner.strategy = :transaction
end
这将清除两个数据库,您的测试将全部顺利运行。
您应该能够运行查询以及has_many通过。
不同的主机
如果您的数据库位于不同的主机上,您可以查看St-Elsewhere gem。它已经过时了,但对如何处理这个问题有了很好的理解。
我希望这有帮助!