多租户Rails模型继承

时间:2017-11-15 19:02:10

标签: ruby-on-rails ruby ruby-on-rails-4 inheritance multi-tenant

我的多租户"客户" model表示来自" org _#{current_user.org.id} _customers"中多个表的数据。模式(即org_1_customers,org_2_customers等)。我使用RequestStore gem来存储current_user的ORG_ID。

所以,问题是要访问CURRENT组织的数据,我必须明确地调用" Customer.org" (即Customer.org.where(...)。load)。这需要重写大量代码,并记住添加" org"每次我访问数据。

我的问题是:有没有办法让我能够访问" Customer.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

P.S。由于租户之间的表格字段不同,我的应用程序确实需要多个客户表!

1 个答案:

答案 0 :(得分:0)

我想出了一个解决方案!它按照惯例在应用程序的任何位置对行进行范围调整,我对此感到满意:

# 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 gem和模型逻辑,转而使用控制器:

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

非常感谢匿名评论者,因为他删除了他的回复。请不要删除有用信息的好回复!我没有什么可以投票的。洛尔。