2

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

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

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

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

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

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

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

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

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

我可以使用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.idandrole.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最好的方法吗?

谢谢!

4

4 回答 4

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 更难管理。如果您确实需要通过应用程序更新角色标签,那么您需要使用架构或其他一些数据库选项

于 2013-08-13T13:42:06.037 回答
1

解决标签问题的一个好方法是使用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'

我编写了一个微型,可以轻松使用上述特定于租户的翻译。

https://github.com/ElMassimo/i18n_multitenant

该库旨在使用静态.yml文件,但它也应该适用于大多数用于.I18n

于 2017-01-26T15:10:30.493 回答
0

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

1.) 表范围

这样,每个模型都有一个额外的belongs_to :tenant关联,它tenant_id为每个表添加一个列。然后,您必须确保确定所有查询的范围(Foo.where(tenant_id: current_tenant)...例如 or 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中进行了广泛的介绍。

于 2013-08-13T13:38:32.403 回答
0

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

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

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

于 2013-08-13T14:12:34.323 回答