方法
def self.for_tenant(tid)
self.table_name = "products_" + tid.to_s
self
end
不过,它有一个副作用:它会更改的表名
Product
班级。例如,当以后在同一请求中使用此类时:
Product.where(name: "My other product") ...
products
如你所料;它将保持不变的变化
for_tenant
方法。
为了避免这种模糊性并保持代码干净,您可以使用另一种策略:
1) 定义一个包含租户分区所有工作逻辑的模块:
# app/models/concerns/partitionable.rb
module Partitionable
def self.included(base)
base.class_eval do
def self.tenant_model(tid)
partition_suffix = "_#{tid}"
table = "#{table_name}#{partition_suffix}"
exists = connection.select_one("SELECT EXISTS (SELECT 1 FROM pg_tables WHERE schemaname = 'public' AND tablename = '#{table}')")
unless exists['exists'] == 't' || exists['exists'] == true # different versions of pg gem give different answers
return self # returning original model class
end
class_name = "#{name}#{partition_suffix}"
model_class = Class.new(self)
model_class.define_singleton_method(:table_name) do
table
end
model_class.define_singleton_method(:name) do
class_name
end
model_class
end
end
end
end
2) 在模型类中包含此模块:
class Product < PostgresDatabase
include Partitionable
...
end
3) 使用方法与预期相同:
Product.tenant_model(TENANT_ID).where(name: "My product")...
那里发生了什么:
tenant_model(TENANT_ID)
为ID为的租户创建另一个模型类
TENANT_ID
Product_<TENANT_ID>
,使用表格
products_<TENANT_ID>
产品
班级。所以它可以像普通模型一样使用。和阶级
它本身保持不变:它的
table_name
产品
.