【问题标题】:Dynamic ActiveRecord model labelling for Rails multi tenant applicationsRails 多租户应用程序的动态 ActiveRecord 模型标签
【发布时间】:2013-08-13 11:44:39
【问题描述】:

我正在 Rails 中构建一个多租户 Web 应用程序,需要为我的一些模型对象提供特定于租户的标签。

这是一个虚构的例子来描述我的意思:

我有一个角色模型对象,每个租户应该有不同的标签。

class Role < ActiveRecord::Base
  has_and_belongs_to_many :users
  validates_presence_of :name
end

在摄影租户中,我需要将可用角色名称列为:

  • 版主
  • 专家
  • 学徒
  • 查看者

在 Journalism 租户中,我需要将可用的角色名称列为:

  • 编辑器
  • 副主编
  • 记者
  • 读者

本质上,应用程序中始终存在四个级别的权限,但在不同的租户中,每个角色只是具有不同的名称。所以在上面的例子中,摄影Moderator和新闻Editor拥有相同的权限,只是标签不同。

我可以使用has_many :through 关联,但我宁愿避免为了获得角色标签而必须加入三个表。

class Tenant < ActiveRecord::Base
  has_many :roles, :through => :tenant_roles
end

class TenantRole < ActiveRecord::Base
  belongs_to :tenant
  belongs_to :role
  validates_presence_of :name 
end

class Role < ActiveRecord::Base
  has_many :tenants, :through => :tenant_roles
end

我还考虑过将角色标签存储在 Redis 中(由于其他原因,我已经有了它)并使用 current_tenant.idrole.id 作为键。这应该很快,但这是个坏主意吗?

class Role < ActiveRecord::Base

  @tenant_roles = Redis::Set.new('tenant_roles') 

  def name(current_tenant)
    @tenant_roles["#{current_tenant.id}-#{self.id}"]
  end

end

关于最佳方式的任何其他想法?使用has_many :though 是最好的方法吗?

谢谢!

【问题讨论】:

  • 您可以使用 i18n 翻译。另外:这并不是大多数人在使用该术语时所指的真正意义上的多租户。
  • 谢谢 Mike - 我曾经想过使用 i18n 翻译,但它似乎有点错误,因为它不是严格意义上的翻译。你能给我一些建议吗?
  • Ps - 我正在使用 Postgres,以防万一。
  • 如何判断观看的人是“摄影”还是“新闻”?
  • 他们将被注册为不同租户的用户。现在我使用子域来确定当前租户。

标签: ruby-on-rails activerecord model label multi-tenant


【解决方案1】:

我想说最好的办法是使用 i18n,因为它只是一个标签问题。额外的好处是他们已经准备好在时机成熟时为您进行实际翻译。

你可以这样设置翻译文件

  en:
    tenant_roles:
      tenant1:
        role1: "Moderator"
        role2: "Expert"
      tenant2:
        role1: "Editor"
        role2: "Sub-Editor"

然后在助手中访问这样的标签

def tenant_role_label tenant_name role_name
    t("tenant_roles.#{tenant_name}.#{role_name}")
end

您当然需要确保tenant_name 和role_name 不会更改。也许使用蛞蝓来做到这一点,所以它不依赖于名称或 ID。

我建议的另一个选择是使用模式,但根据我的经验,它比使用 i18n 更难管理。如果您确实需要通过应用程序更新角色标签,那么您需要使用架构或其他一些数据库选项

【讨论】:

  • 感谢stallard,这正是我想要的。一旦这种情况有所进展,我可能会考虑使用 Redis 来存储角色标签,正如 Ryan Bates 所描述的那样railscasts.com/episodes/256-i18n-backends
【解决方案2】:

解决标签问题的一个好方法是使用I18n。您可以为每个租户定义特定的语言,然后只翻译角色名称。在您的示例中,请考虑以下内容:

en-PHOTOGRAPHY:
  role1: 'Moderator'
  role2: 'Expert'
  role3: 'Apprentice'
  role4: 'Viewer'

en-JOURNALISM:
  role1: 'Editor'
  role2: 'Sub-Editor'
  role3: 'Journalist'
  role4: 'Reader'

# Optional
en:
  role1: 'Super-Admin'
  role2: 'Admin'
  role3: 'User'
  role4: 'Visitor'

我写了一个微library,它可以很容易地使用上面那些特定于租户的翻译。

https://github.com/ElMassimo/i18n_multitenant

该库旨在使用静态.yml 文件,但它也应该适用于大多数可用于I18nSQLRedis 后端。

【讨论】:

    【解决方案3】:

    您的问题集中在这个角色标签示例上,所以我不知道您是否希望您的应用程序像大多数多租户应用程序一样运行,也就是说,所有内容都以租户为范围,或者如果这是唯一的实例你需要这个功能。我将简单介绍一下典型的多租户解决方案,其中常见的有两种:

    1.) 表范围

    这样,每个模型都有一个额外的belongs_to :tenant 关联,它为每个表添加一个tenant_id 列。然后,您必须确保确定所有查询的范围(例如 Foo.where(tenant_id: current_tenant)...current_tenant.foos..),或者您可以使用其中一种解决方案自动执行此操作,例如 wireframe/multitenant 或我的(非常 WIP )mikecmpbll/cohabit。这样您的角色表将如下所示:

    id | name          | tenant_id
    ------------------------------
    1  | Moderate      |        1
    1  | Expert        |        1
    1  | Apprentice    |        1
    1  | Viewer        |        1
    1  | Editor        |        2
    1  | Subeditor     |        2
    1  | Journalist    |        2
    1  | Reader        |        2
    

    2.) 模式

    在支持模式的数据库(例如 PostgreSQL)中,您可以切换模式而不是确定模型的范围。如果您使用 pg,这被广泛认为是更好的解决方案,并且在 Ryan Bigg 的Multitenancy with Rails 中有广泛的介绍。

    【讨论】:

      【解决方案4】:

      如果每个角色都有主名称,我会使用序列化哈希

      class Tenant < ActiveRecord::Base
          serialize :roles, Hash
      end
      
      journalism = Tenant.create :roles => { 
          role1: 'Moderator', role2: 'Expert', role3: 'Apprentice', role4: 'Viewer'
      }
      
      photography = Tenant.create :roles => { 
          role1: 'Editor', role2: 'Sub-Editor', role3: 'Journalist', role4: 'Reader'
      } 
      

      或者您甚至可以不使用角色索引:

      class Tenant < ActiveRecord::Base
          serialize :roles, Array
      end
      
      journalism = Tenant.create :roles => ['Moderator', 'Expert', 'Apprentice', 'Viewer']
      photography = Tenant.create :roles => ['Editor', 'Sub-Editor', 'Journalist', 'Reader']
      

      然后您可以使用位掩码来存储单个用户的角色,例如

      journalist.roles = "0001" # viewer
      journalist.roles = "0101" # expert and viewer
      

      注意:我确信有比字符串更好的方式来存储/使用位掩码,但我会留给你 :)

      【讨论】:

        猜你喜欢
        • 2016-12-12
        • 2018-08-29
        • 2013-03-24
        • 1970-01-01
        • 1970-01-01
        • 2019-11-12
        • 1970-01-01
        • 1970-01-01
        • 2011-02-16
        相关资源
        最近更新 更多