Модель 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

Большое спасибо анонимному комментатору, который с тех пор удалил свой ответ. Пожалуйста, не удаляйте ваши хорошие ответы с полезной информацией! Мне нечего возражать. Лол.

Другие вопросы по тегам