Rails:通过相关模型的一个模型的多个“类型”?

时间:2010-05-26 19:17:25

标签: ruby-on-rails model

我的应用中有User型号,我想存储基本的用户信息,例如电子邮件地址,姓名,电话号码等。

我的系统中也有许多不同类型的用户,包括销售代理,客户,访客等。

我希望能够使用相同的User模型作为所有其他模型的基础,这样我就不必在一个模型中包含所有相关角色的所有字段,并且可以根据需要委派(减少重复的数据库字段,并提供从一种类型的用户更改为另一种类型的轻松移动性)。

所以,我想要的是:

User
-- first name
-- last name
-- email
--> is a "client", so
---- client field 1
---- client field 2
---- client field 3

User
-- first name
-- last name
-- email
--> is a "sales agent", so
---- sales agent field 1
---- sales agent field 2
---- sales agent field 3

and so on...

此外,当新用户注册时,我希望新用户自动被分配“客户端”角色(我在这里谈的是数据库字段,而不是授权,但我希望最终将此逻辑包括在内)我的用户授权)。我有一个多步注册向导,我正在尝试用wizardly构建。第一步很简单,因为我只是简单地调用基础User模型中包含的字段(例如first_nameemail),但第二步是棘手的​​,因为它应该是从相关模型中调用字段(例如 - 按照上面的示例 - 模型client包含字段client_field_1client_field_2,就好像这些字段是User的一部分一样)。

这有意义吗?如果这一点根本不清楚,请告诉我,我会尝试以不同的方式解释它。

任何人都可以帮我吗?我该怎么做?

2 个答案:

答案 0 :(得分:3)

根据tadman的建议,STI可能非常适合您的要求,如果您使用的是ActiveRecord(从Rails 3开始,很容易更改ORM)。基本信息可在AR documentation page上找到,但这里有一些额外的信息w.r.t.你的目标:

  • 为每个文件定义一个模型。否则会有一些初始化问题。假设客户端在一个文件中从User all继承,只要没有调用一次User构造函数,就无法创建Client对象。每个模型一个文件可以解决问题。
  • 通过层次结构的所有属性都是在顶级类中一次性定义的。这是针对性能问题,但在博客文章中似乎令许多人感到不安。简而言之,Ruby代码是面向对象的,并且适当地封装了属性。数据库包含单个表中的所有内容,并使用额外的“类型”列来区分它们在层次结构中的位置。这只是表示关系数据库中继承树的“技巧”。我们必须意识到ORM映射并不简单。 image on this site from Martin Fowler可能有助于了解情况。

此外,您希望任何新用户都是客户端,客户端从User继承。为此,您可以简单地将任何新用户实例化为客户端。在用于创建用户的控制器中:

user = Client.new
# Do something to user
user.save
#=> <Client id: 1, name: "Michael Bolton", email: "mike@bolton.net", created_at: "2010-05-30 03:27:39", updated_at: "2010-05-30 03:27:39">

使用ActiveRecords时,以上所有内容对Rails 3仍然有效。

答案 1 :(得分:1)

看起来你有两种合理的方法,但这取决于你的要求的细微差别。

您可以使用单表继承(STI)来执行您想要的操作,其中User仅是名为SalesAgent或Client等的其他人的基类。这些子类中的每一个都可以定义它们自己的验证。您需要的只是一个名为“type”的字符串列,ActiveRecord将完成其余的工作:

class User < ActiveRecord::Base
end

class Agent < User
end

另一种方法是拥有一些自由格式字段,您可以在其中存储各种相关数据,并在运行时以不同方式对它们进行不同的解释。你可以像这样构造它:

class User < ActiveRecord::Base
  has_one :agent_role,
    :dependent => :destroy
end

class AgentRole < ActiveRecord::Base
  belongs_to :user

   # Represents the agent-specific role fields
end

这将具有允许多个角色的优势,如果您使用has_many,则使用相同类型的多个角色。