Ruby on rails Rails:多态或STI或其他用于用户管理的东西?

Ruby on rails Rails:多态或STI或其他用于用户管理的东西?,ruby-on-rails,polymorphic-associations,sti,Ruby On Rails,Polymorphic Associations,Sti,我一直把头撞在墙上,试图把我的头绕在这周围,所以任何指导都将不胜感激 我希望用户系统设置反映以下层次结构: User |- email address |- password |- billing information |- contact information |- account preferences | |=> Agent |=> - agent-specific information |=> - has_many Users |=> - belongs_t

我一直把头撞在墙上,试图把我的头绕在这周围,所以任何指导都将不胜感激

我希望用户系统设置反映以下层次结构:

User
|- email address
|- password
|- billing information
|- contact information
|- account preferences
|
|=> Agent
|=> - agent-specific information
|=> - has_many Users
|=> - belongs_to Manager
|
|=> Manager
|=> - manager-specific information
|=> - has_many Agents, Users
|
|=> Administrator
|=> - can manage everything
我已经有了一个
用户
模型和设置来处理身份验证和授权,因此我知道如何使用角色将用户类型限制为特定操作,等等

我不知道的是如何在Rails代码和数据库中组织这些子类关系。从上面可以看到,
代理
管理者
管理员
都共享
用户
中包含的信息,但每个都有与之相关的附加功能和信息

我读了一些关于,和

如果我使用STI,
用户
表必须包含我的所有[
代理
/
管理者
/
管理员
]特定信息的字段,对吗?这会使我的
User
表变得庞大,这是我想要避免的。相反,如果我使用多态,那么我不需要在所有其他类型的
User
子类表中复制
User
中的所有公共信息吗

更让我困惑的是,我无法理解上述问题的答案是如何与子类之间的关系联系在一起的(比如,一个
管理者
有许多
代理
,但它们都是
用户
的子类……)


如果有人能给我一个详细的答案,让我明白这一点,并充分考虑到代码可读性和数据完整性,我会非常感激,这简单地解释了(就像对Rails新手一样)为什么a是最好的方法,为什么B或n——相比之下——不是这种情况下的好方法,这给出了实现上述关系的示例代码。我想解决这个问题,但更重要的是,我想了解解决方案有效的原因

我不认为有一个简单的解释为什么任何方法在任何情况下都是最好的。关于堆栈溢出的大量问题是关于如何设计它们的模型之间的关系的问题。这是一个复杂的话题,正确的解决方案需要对你正在解决的问题有深入的了解。即使这样,你可能也不会在头几次做对

到目前为止,最好的方法是在这个问题上完全使用TDD/BDD,并让测试/规范驱动您的设计。当你找到更好的方法时,不要害怕重构。大多数情况下,只有在尝试了几个错误的解决方案后,你才会看到正确的解决方案。您将了解边缘案例。正如沃德·坎宁安(Ward Cunningham)在他的“技术债务”比喻中所说的:“事后重构,就好像你从一开始就知道自己在做什么。”。不过,一定要在事后进行验收测试,以验证其性能

更具体地解决你的问题。还有第三种选择,即完全拆分类,每个类都有自己的表。我在目前的项目中尝试过,我喜欢它。如果在您的业务领域中没有意义,那么您不需要定义用户之类的东西。如果它们具有共享行为,则使用mixin。最重要的警告是,让他们通过同一个表单登录已经不是一件简单的事了

我有管理员、招聘人员、供应商和访问者模型。它们都是独立的模型,与mixin共享一些行为。它们都有自己的名称空间控制器来执行操作。例如,管理员的所有操作都在后端命名空间中。还有一个名称空间的应用程序控制器。后端::ApplicationController只是在\u过滤器之前指定
:authorize\u admin。没有转换,没有复杂的案例陈述,什么都没有

你需要特别注意惯例。如果你在模型中使用相同的名字,你的混音会变得非常容易。请阅读ActiveSupport::Concern,以使mixin更易于使用。我有一个这样的混音:

module Account
  extend ActiveSupport::Concern
  included do
    devise :database_authenticatable, :trackable, :recoverable, :rememberable
  end
end
class Backend::ApplicationController < ::ApplicationController
  layout "backend"
  before_filter :authenticate_admin!
  def current_ability
    @current_ability ||= AdminAbility.new(current_admin)
  end
end
在我的路线中:

devise_for :recruiters
devise_for :suppliers
# etc...
app/controllers/backend/application\u controller.rb
如下所示:

module Account
  extend ActiveSupport::Concern
  included do
    devise :database_authenticatable, :trackable, :recoverable, :rememberable
  end
end
class Backend::ApplicationController < ::ApplicationController
  layout "backend"
  before_filter :authenticate_admin!
  def current_ability
    @current_ability ||= AdminAbility.new(current_admin)
  end
end
类后端::ApplicationController<::ApplicationController
布局“后端”
筛选前:验证\u管理员!
def当前_能力
@当前|||=管理能力。新建(当前|管理)
结束
结束
因此,总结一下。任何架构都可以。STI和多态性有它们的位置,但一定要根据您的领域对您的体系结构进行建模。Ruby是一种非常灵活的语言,您可以充分利用它。Desive和CanCan是优秀的宝石,可以轻松处理这些情况。我向您展示了我目前正在进行的一个项目的解决方案。它对我很有效,但我不能说它是否对你有效。当你觉得自己做了一个糟糕的决定时,不要害怕去尝试和重构,而不是继续修补你原来的想法


说到性传播感染和人际关系:他们的合作也非常出色。您可以定义从一个子类到另一个子类的关系。这一切都会像预期的那样工作。

可能会让人想得太多,但我不相信你想做的事情是非常复杂的

STI在这里可能不是最好的主意,因为您将用户限制为不超过这些关键角色中的一个

至于存储特定于成员身份的信息,我不确定在为STI添加列时您的表是否会“庞大”。如果它们为null,则它们实际上并不消耗任何资源。如果您不需要对该数据进行选择,您可以考虑使用单个列<代码> RoLyIdvase<代码>,然后使用<代码>序列化:RoLyIdvase<代码>,这样您就可以定义哈希并将其存储在列中。

另一个选项是重写方法,以便它们在响应时检查角色成员身份,即:

has_many :agents
def agents
  return nil unless self.has_role(:manager) 
  return self[:agents]
end
代表