简体   繁体   English

多租户Rails模型继承

[英]Multitenant Rails model Inheritance

My multi-tenant "Customer" model represents data from multiple tables in a "org_#{current_user.org.id}_customers" pattern (ie org_1_customers, org_2_customers, etc). 我的多租户“客户”模型以“ org _#{current_user.org.id} _customers”模式(即org_1_customers,org_2_customers等)表示来自多个表的数据。 I'm using RequestStore gem to store current_user's ORG_ID. 我正在使用RequestStore gem存储current_user的ORG_ID。

So, the problem is that to access data of CURRENT organization, I have to explicitly call "Customer.org" (ie Customer.org.where(...).load). 因此,问题在于,要访问当前组织的数据,我必须显式调用“ Customer.org”(即Customer.org.where(...)。load)。 Which requires rewriting A LOT of code, and remembering to add "org" every time I access the data. 这需要重写大量代码,并且每次访问数据时都要记住添加“ org”。

MY QUESTION IS: Is there a way to make it so that I could access "Customer.org" by calling "Customer", so I would use "Customer" for current tenant's/org's customers, and "Customer.select_org(7)" for other tenant's/org's customers? 我的问题是:有没有一种方法可以使我可以通过调用“客户”来访问“ Customer.org”,因此我可以将“ Customer”用于当前租户/组织的客户,以及“ 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 My application does require multiple Customer tables due to differences in table fields between tenants! PS由于租户之间的表字段不同,我的应用程序确实需要多个Customer表!

I've come up with a solution! 我想出了一个解决方案! It scopes the rows by convention anywhere in the app and I'm satisfied with it: 它按约定在应用程序中的任何位置划定行的范围,我对此感到满意:

# 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

To Do this, first, I segregated the "scoping" into a class: 为此,首先,我将“作用域”分为一个类:

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

while getting rid of the RequestStore gem and model logic in favor of the controller: 同时摒弃了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

Model inheritance is still there: 模型继承仍然存在:

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

Thanks a lot to an anonymous commenter who since deleted his response. 非常感谢匿名评论者此后删除了他的回复。 Please don't delete your good responses with useful information! 请不要删除包含有用信息的良好回复! I have nothing to upvote. 我没有什么可投票的。 Lol. 大声笑。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM