Модель Multitenant Rails Наследование
Моя мультитенантная модель "Клиент" представляет данные из нескольких таблиц в виде "org_#{current_user.org.id}_customers" (т. Е. Org_1_customers, org_2_customers и т. Д.). Я использую гем RequestStore для хранения ORG_ID current_user.
Таким образом, проблема в том, что для доступа к данным CURRENT организации мне нужно явно вызвать "Customer.org" (т. Е. Customer.org.where(...). Load). Который требует переписать много кода, и не забывая добавлять "org" каждый раз, когда я получаю доступ к данным.
МОЙ ВОПРОС: Есть ли способ сделать так, чтобы я мог получить доступ к "Customer.org", позвонив "Клиент", чтобы я использовал "Клиент" для клиентов текущего арендатора / org и "Customer.select_org(7)" для клиентов других арендаторов / организаций?
class ApplicationController < ActionController::Base
before_filter :find_organization
private
def find_organization
RequestStore[:current_org] = current_user ? current_user.org : nil
end
end
class SegregatedMultitenantModel < ActiveRecord::Base
self.abstract_class = true
def self.select_org(org_id)
@subdomain_classes ||= {}
unless @subdomain_classes[org_id]
@subdomain_classes[org_id] ||= Class.new(self)
@subdomain_classes[org_id].table_name = "org_#{org_id}_#{self.table_name}" # This needs sanitizing, of course
@subdomain_classes[org_id].reset_column_information
end
@subdomain_classes[org_id]
end
def self.org
if RequestStore[:current_org].nil?
raise "No Organization Selected For #{self.table_name}"
else
self.select_org(RequestStore[:current_org].id)
end
end
end
class Customer < SegregatedMultitenantModel
end
PS Мое приложение требует нескольких таблиц Customer из-за различий в полях таблиц между арендаторами!
1 ответ
Я придумал решение! По соглашению он ограничивает строки в любом месте приложения, и я доволен этим:
# Returns first row from "org_#{current_user.org.id}_customers" table
MT::Customer.first
# Returns first row from "org_3_customers" table
MT::Customer.select_org(3).first
Для этого я сначала разделил "область видимости" на класс:
class MT
@@segregated_organization_models = {}
def self.init(organizations, current_org, *options)
organizations.each do |org|
options[0][:segregated_models].each do |class_object|
# Create new model class
@@segregated_organization_models[org.id] ||= {}
a = Class.new(class_object)
@@segregated_organization_models[org.id][class_object.to_s.to_sym] = a
# Set correct table name
@@segregated_organization_models[org.id][class_object.to_s.to_sym].table_name = "org_#{org.id}_#{class_object.table_name}"
# Set default model class to current organization's model class
if org.id === current_org.id
self.const_set(class_object.to_s.to_sym, @@segregated_organization_models[org.id][class_object.to_s.to_sym])
end
end
end
end
end
избавляясь от драгоценного камня RequestStore и логики модели в пользу контроллера:
class ApplicationController < ActionController::Base
before_filter :find_organization
private
# Multitenancy helpers
def find_organization
MT.init(Organization.all, current_user.org,
segregated_models: [
ArticleCategory, Article, CustomArticleField,
Customer, CustomerChange, CustomerLedgerItem
]
)
end
end
Модель наследования все еще существует:
class SegregatedMultitenantModel < ActiveRecord::Base
self.abstract_class = true
def self.select_org(org_id)
MT.class_variable_get(:@@segregated_organization_models)[org_id][self.to_s.demodulize.to_sym]
end
end
class Customer < SegregatedMultitenantModel
self.abstract_class = true
self.table_name = "customers"
end
Большое спасибо анонимному комментатору, который с тех пор удалил свой ответ. Пожалуйста, не удаляйте ваши хорошие ответы с полезной информацией! Мне нечего возражать. Лол.